{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "c683cc9d",
   "metadata": {
    "vscode": {
     "languageId": "raw"
    }
   },
   "source": [
    "# Arkitect Cookbook: 从零开始构建您的第一个 AI Agent\n",
    "\n",
    "欢迎来到 Arkitect 框架！这个 Cookbook 将引导您从基础概念开始，逐步构建一个功能完整的 AI Agent。\n",
    "\n",
    "## 学习目标\n",
    "\n",
    "通过这个 Cookbook，您将学会：\n",
    "\n",
    "1. **理解 Context**：掌握 Arkitect 的核心概念 - 对话状态管理器\n",
    "2. **构建 Agent**：学会如何将 Context 配置为特定任务的智能代理\n",
    "3. **集成工具**：为您的 Agent 添加自定义功能\n",
    "4. **精确控制**：通过参数精确调整 Agent 的行为模式\n",
    "5. **调试观察**：使用 Hooks 深入了解 Agent 的内部工作机制\n",
    "\n",
    "让我们开始这个令人兴奋的旅程！\n",
    "\n",
    "---\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cfa829bb",
   "metadata": {},
   "source": [
    "## Arkitect Cookbook 目录\n",
    "\n",
    "1. 第 0 章：环境准备与设置\n",
    "    - 安装依赖\n",
    "    - 配置 API Key\n",
    "    - 导入必要的模块\n",
    "    - 框架核心概念预览\n",
    "2. 第 1 章：核心概念 - Context（对话管理器）\n",
    "    - 创建您的第一个 Context\n",
    "    - Context 的\"记忆\"能力\n",
    "    - ArkMessage：消息的数据结构\n",
    "    - State：对话状态的管理者\n",
    "    - System Prompt 的威力\n",
    "    - 第 1 章总结\n",
    "3. 第 2 章：构建您的第一个 Agent\n",
    "    - 工具系统的核心概念\n",
    "    - 定义工具函数\n",
    "    - 将函数转换为 ChatCompletionTool\n",
    "    - Agent 的创建与工具集成\n",
    "    - 工具调用与流式响应\n",
    "    - ToolChunk：流式响应中的工具调用\n",
    "    - ContextInterruption：异常处理机制\n",
    "    - 第 2 章总结\n",
    "4. 第 3 章：精确控制 Agent 行为 - ArkChatParameters\n",
    "    - 参数控制的核心价值\n",
    "    - ArkChatParameters：参数控制的核心\n",
    "    - 参数实验与优化\n",
    "    - 综合参数控制实验\n",
    "    - 特殊参数演示：thinking\n",
    "    - ArkChatRequest：请求的完整生命周期\n",
    "    - ArkChatResponse：响应和使用统计\n",
    "    - 第 3 章总结\n",
    "5. 第 4 章：高级调试 - 使用 Hooks 观察 Agent\n",
    "    - Hooks 的核心价值\n",
    "    - 四种主要 Hook 类型\n",
    "    - 创建和应用自定义 Hooks\n",
    "    - 性能监控与异常处理 Hook\n",
    "    - Hook 在自动化测试中的应用\n",
    "    - Hook 最佳实践\n",
    "    - 第 4 章总结\n",
    "6. Q&A：常见问题和解决方案\n",
    "7. 结语：您的 Arkitect 之旅\n",
    "\n",
    "---"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d636e771",
   "metadata": {
    "vscode": {
     "languageId": "raw"
    }
   },
   "source": [
    "## 第 0 章：环境准备与设置\n",
    "\n",
    "在开始构建您的第一个 Agent 之前，让我们确保您的环境已经正确配置。\n",
    "\n",
    "### 步骤 1：安装依赖\n",
    "\n",
    "首先，让我们安装 Arkitect 框架及其必要的依赖项：\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c5b0e80f",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Arkitect 框架已成功安装！\n",
      "版本：\n"
     ]
    }
   ],
   "source": [
    "# 安装 Arkitect 框架\n",
    "# 如果您还没有安装，请取消注释下面的行\n",
    "# !pip install arkitect\n",
    "\n",
    "import os\n",
    "import sys\n",
    "\n",
    "# 设置项目路径\n",
    "project_root = os.path.abspath(os.path.join(os.getcwd(), \"../..\"))\n",
    "sys.path.append(project_root)\n",
    "\n",
    "# 验证安装\n",
    "try:\n",
    "    import arkitect\n",
    "\n",
    "    print(\"Arkitect 框架已成功安装！\")\n",
    "    print(\n",
    "        f\"版本：{\n",
    "            arkitect.__version__ if hasattr(arkitect, '__version__') else '开发版本'\n",
    "        }\"\n",
    "    )\n",
    "except ImportError:\n",
    "    print(\"请先安装 Arkitect 框架：pip install arkitect\")\n",
    "    raise"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "90ea0788",
   "metadata": {
    "vscode": {
     "languageId": "raw"
    }
   },
   "source": [
    "### 步骤 2：配置 API Key\n",
    "\n",
    "为了使用 Arkitect 框架，您需要配置您的 API Key。这里我们提供一个安全的方式来设置您的 API Key：\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7b166c7e",
   "metadata": {},
   "outputs": [],
   "source": [
    "import getpass\n",
    "\n",
    "# 检查是否已经设置了 API Key\n",
    "if \"ARK_API_KEY\" in os.environ and os.environ[\"ARK_API_KEY\"] != \"YOUR_API_KEY_HERE\":\n",
    "    print(\"✅ API Key 已配置\")\n",
    "    print(f\"🔑 当前 API Key 前缀：{os.environ.get('ARK_API_KEY', 'Not Set')[:10]}...\")\n",
    "else:\n",
    "    print(\"🔐 请输入您的 API Key：\")\n",
    "    api_key = getpass.getpass(\"API Key: \")\n",
    "\n",
    "    # 基本验证 - 确保不是默认占位符\n",
    "    if api_key.strip() == \"\" or api_key == \"YOUR_API_KEY_HERE\":\n",
    "        print(\"❌ 请输入有效的 API Key\")\n",
    "        raise ValueError(\"无效的 API Key\")\n",
    "\n",
    "    # 设置环境变量\n",
    "    os.environ[\"ARK_API_KEY\"] = api_key\n",
    "    print(\"✅ API Key 已成功设置！\")\n",
    "    print(f\"🔑 当前 API Key 前缀：{os.environ.get('ARK_API_KEY', 'Not Set')[:10]}...\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ce4a1464",
   "metadata": {
    "vscode": {
     "languageId": "raw"
    }
   },
   "source": [
    "### 步骤 3：导入必要的模块\n",
    "\n",
    "让我们导入本 Cookbook 中将要使用的核心模块：\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "ccb9df1d",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "✅ 所有必要的模块已成功导入！\n",
      "🚀 现在您可以开始构建您的第一个 Agent 了！\n"
     ]
    }
   ],
   "source": [
    "import logging\n",
    "from typing import Any, Optional\n",
    "\n",
    "# 导入 Arkitect 核心组件\n",
    "from arkitect.core.component.context.context import Context\n",
    "\n",
    "# 导入 Hooks 相关类（用于高级调试）\n",
    "from arkitect.core.component.context.hooks import (\n",
    "    PostLLMCallHook,\n",
    "    PostToolCallHook,\n",
    "    PreLLMCallHook,\n",
    "    PreToolCallHook,\n",
    ")\n",
    "from arkitect.core.component.context.model import State\n",
    "\n",
    "# 导入用于自定义 Agent 行为的参数类\n",
    "from arkitect.types.llm.model import (\n",
    "    ArkChatParameters,\n",
    "    ArkMessage,\n",
    "    ChatCompletionTool,\n",
    ")\n",
    "\n",
    "# 静默HTTP相关的日志输出，避免干扰教程体验\n",
    "logging.getLogger(\"httpx\").setLevel(logging.WARNING)\n",
    "logging.getLogger(\"urllib3.connectionpool\").setLevel(logging.WARNING)\n",
    "logging.getLogger(\"volcenginesdkarkruntime\").setLevel(logging.WARNING)\n",
    "\n",
    "print(\"✅ 所有必要的模块已成功导入！\")\n",
    "print(\"🚀 现在您可以开始构建您的第一个 Agent 了！\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "02eb853b",
   "metadata": {
    "vscode": {
     "languageId": "raw"
    }
   },
   "source": [
    "### 步骤 4：框架核心概念预览\n",
    "\n",
    "在深入学习之前，让我们先了解一下 Arkitect 框架的核心概念：\n",
    "\n",
    "#### 🧠 **Context（对话管理器）**\n",
    "- 框架的核心组件，管理对话状态和历史\n",
    "- 自动处理多轮对话，无需手动管理消息历史\n",
    "- 作为应用程序与大语言模型之间的桥梁\n",
    "\n",
    "#### 📝 **ArkMessage（消息对象）**\n",
    "- 表示对话中的单个消息\n",
    "- 包含角色（user/assistant/system/tool）和内容\n",
    "- 是 Context 内部存储和处理消息的基本单位\n",
    "\n",
    "#### 🛠️ **ChatCompletionTool（工具系统）**\n",
    "- 将 Python 函数转换为 LLM 可理解的工具描述\n",
    "- 支持自动参数解析和函数调用\n",
    "- 让 Agent 能够执行实际的操作\n",
    "\n",
    "#### ⚙️ **ArkChatParameters（参数控制）**\n",
    "- 精确控制 LLM 的行为（温度、最大token数等）\n",
    "- 支持思考模式、停止词等高级功能\n",
    "- 让您能够针对不同任务优化 Agent 性能\n",
    "\n",
    "#### 🔍 **Hooks（调试观察）**\n",
    "- 提供 Agent 执行过程的深度观察能力\n",
    "- 支持性能监控、错误处理和自定义逻辑\n",
    "- 是调试和优化 Agent 的重要工具\n",
    "\n",
    "让我们通过一个简单的例子来预览这些概念：\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "bc0a6e17",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "🎯 Arkitect 框架核心概念预览\n",
      "==================================================\n",
      "\n",
      "📝 ArkMessage 类示例：\n",
      "   类型：ArkMessage\n",
      "   角色：user\n",
      "   内容：Hello, Arkitect!\n",
      "\n",
      "🛠️ ChatCompletionTool 类示例：\n",
      "   工具名称：sample_tool\n",
      "   工具描述：一个示例工具函数\n",
      "   参数结构：['message']\n",
      "\n",
      "⚙️ ArkChatParameters 类示例：\n",
      "   温度设置：0.7\n",
      "   最大token：100\n",
      "   参数类型：ArkChatParameters\n",
      "\n",
      "🧠 Context 类概览：\n",
      "   - 管理对话状态和历史\n",
      "   - 支持工具集成\n",
      "   - 提供参数控制\n",
      "   - 支持 Hook 系统\n",
      "\n",
      "✨ 接下来我们将深入学习每个概念的详细用法！\n",
      "==================================================\n"
     ]
    }
   ],
   "source": [
    "async def preview_core_concepts():\n",
    "    \"\"\"快速预览 Arkitect 框架的核心概念\"\"\"\n",
    "\n",
    "    print(\"🎯 Arkitect 框架核心概念预览\")\n",
    "    print(\"=\" * 50)\n",
    "\n",
    "    # 1. 展示 ArkMessage 类\n",
    "    print(\"\\n📝 ArkMessage 类示例：\")\n",
    "    sample_message = ArkMessage(role=\"user\", content=\"Hello, Arkitect!\")\n",
    "    print(f\"   类型：{type(sample_message).__name__}\")\n",
    "    print(f\"   角色：{sample_message.role}\")\n",
    "    print(f\"   内容：{sample_message.content}\")\n",
    "\n",
    "    # 2. 展示 ChatCompletionTool 类\n",
    "    print(\"\\n🛠️ ChatCompletionTool 类示例：\")\n",
    "\n",
    "    def sample_tool(message: str) -> str:\n",
    "        \"\"\"一个示例工具函数\"\"\"\n",
    "        return f\"处理消息：{message}\"\n",
    "\n",
    "    tool = ChatCompletionTool.from_function(sample_tool)\n",
    "    print(f\"   工具名称：{tool.function.name}\")\n",
    "    print(f\"   工具描述：{tool.function.description}\")\n",
    "    print(f\"   参数结构：{list(tool.function.parameters.get('properties', {}).keys())}\")\n",
    "\n",
    "    # 3. 展示 ArkChatParameters 类\n",
    "    print(\"\\n⚙️ ArkChatParameters 类示例：\")\n",
    "    params = ArkChatParameters(temperature=0.7, max_tokens=100)\n",
    "    print(f\"   温度设置：{params.temperature}\")\n",
    "    print(f\"   最大token：{params.max_tokens}\")\n",
    "    print(f\"   参数类型：{type(params).__name__}\")\n",
    "\n",
    "    # 4. 展示 Context 类（不初始化，只展示结构）\n",
    "    print(\"\\n🧠 Context 类概览：\")\n",
    "    print(\"   - 管理对话状态和历史\")\n",
    "    print(\"   - 支持工具集成\")\n",
    "    print(\"   - 提供参数控制\")\n",
    "    print(\"   - 支持 Hook 系统\")\n",
    "\n",
    "    print(\"\\n✨ 接下来我们将深入学习每个概念的详细用法！\")\n",
    "    print(\"=\" * 50)\n",
    "\n",
    "\n",
    "# 运行预览\n",
    "await preview_core_concepts()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c83ab3be",
   "metadata": {
    "vscode": {
     "languageId": "raw"
    }
   },
   "source": [
    "---\n",
    "\n",
    "## 第 1 章：核心概念 - Context（对话管理器）\n",
    "\n",
    "现在让我们深入了解 Arkitect 的核心概念：**Context**。\n",
    "\n",
    "### 什么是 Context？\n",
    "\n",
    "Context 是 Arkitect 框架的核心组件，它充当一个智能的\"对话管理器\"。想象一下，Context 就像一个有记忆的助手，它能够：\n",
    "\n",
    "- 🧠 **记住整个对话历史**：自动跟踪用户和 AI 之间的所有交互\n",
    "- 🔄 **管理多轮对话**：无需手动管理对话状态，Context 会自动处理\n",
    "- 🤖 **连接 AI 模型**：作为您的应用程序与大语言模型之间的桥梁\n",
    "- 📊 **维护状态信息**：通过内部的 State 对象管理所有会话数据\n",
    "\n",
    "让我们通过一个简单的例子来看看 Context 是如何工作的：\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ae630a4b",
   "metadata": {
    "vscode": {
     "languageId": "raw"
    }
   },
   "source": [
    "### 示例 1：创建您的第一个 Context\n",
    "\n",
    "让我们创建一个基本的 Context 实例并进行第一次对话：\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "a4d05eab",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "🤖 创建第一个 Context...\n",
      "\u001b[2m2025-08-05 19:36:47\u001b[0m [\u001b[32m\u001b[1mdebug    \u001b[0m] \u001b[1msingleton class initialized   \u001b[0m \u001b[36mname\u001b[0m=\u001b[35mClientPool\u001b[0m\n",
      "✅ Context 已初始化，使用模型：doubao-seed-1-6-flash-250615\n",
      "\n",
      "🗣️ 开始第一轮对话...\n",
      "👤 用户: 我是一个会飞的西兰花，和我打个招呼吧？\n",
      "🤖 AI: 哇哦，会飞的西兰花你好呀！感觉你好特别呀，快给我讲讲你飞起来的奇妙体验呗~\n",
      "==================================================\n"
     ]
    }
   ],
   "source": [
    "async def create_first_context():\n",
    "    \"\"\"演示基本的 Context 使用方式\"\"\"\n",
    "\n",
    "    # 步骤 1：创建一个 Context 实例\n",
    "    # Context 需要指定要使用的模型\n",
    "    MODEL = \"doubao-seed-1-6-flash-250615\"  # 您可以根据需要更换模型\n",
    "\n",
    "    print(\"🤖 创建第一个 Context...\")\n",
    "    context = Context(model=MODEL)\n",
    "\n",
    "    # 步骤 2：初始化 Context\n",
    "    # 这会建立与模型的连接\n",
    "    await context.init()\n",
    "    print(f\"✅ Context 已初始化，使用模型：{MODEL}\")\n",
    "\n",
    "    # 步骤 3：发送第一条消息\n",
    "    print(\"\\n🗣️ 开始第一轮对话...\")\n",
    "\n",
    "    first_message = \"我是一个会飞的西兰花，和我打个招呼吧？\"\n",
    "\n",
    "    # 使用 Context 发送消息并获取响应\n",
    "    completion = await context.completions.create(\n",
    "        [{\"role\": \"user\", \"content\": first_message}],\n",
    "        stream=True,  # 启用流式响应，可以实时看到输出\n",
    "    )\n",
    "\n",
    "    print(f\"👤 用户: {first_message}\")\n",
    "    print(\"🤖 AI: \", end=\"\")\n",
    "\n",
    "    # 处理流式响应\n",
    "    async for chunk in completion:\n",
    "        if chunk.choices and chunk.choices[0].delta.content:\n",
    "            print(chunk.choices[0].delta.content, end=\"\")\n",
    "\n",
    "    print(\"\\n\" + \"=\" * 50)\n",
    "\n",
    "    return context  # 返回 context 以便后续使用\n",
    "\n",
    "\n",
    "# 运行第一次对话\n",
    "context = await create_first_context()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "450266cb",
   "metadata": {
    "vscode": {
     "languageId": "raw"
    }
   },
   "source": [
    "### 示例 2：Context 的\"记忆\"能力\n",
    "\n",
    "现在让我们看看 Context 最强大的特性之一：**自动记忆管理**。\n",
    "\n",
    "注意：我们将使用**同一个** Context 实例继续对话，这样它就能记住之前的交互内容。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "3a3f3814",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "🧠 测试 Context 的记忆能力...\n",
      "\n",
      "🗣️ 第二轮对话...\n",
      "👤 用户: 我在第一轮对话说我的身份是什么样的？\n",
      "🤖 AI: 在第一轮对话中你说自己是“一个会飞的西兰花”呀。\n",
      "==================================================\n",
      "\n",
      "🗣️ 第三轮对话...\n",
      "👤 用户: 在第一轮对话中我提出了什么请求？\n",
      "🤖 AI: 在第一轮对话中你提出的请求是“和我打个招呼吧？”\n",
      "==================================================\n"
     ]
    }
   ],
   "source": [
    "async def demonstrate_memory(context):\n",
    "    \"\"\"演示 Context 的记忆能力\"\"\"\n",
    "\n",
    "    print(\"🧠 测试 Context 的记忆能力...\")\n",
    "\n",
    "    # 第二轮对话：询问相关问题\n",
    "    print(\"\\n🗣️ 第二轮对话...\")\n",
    "    second_message = \"我在第一轮对话说我的身份是什么样的？\"\n",
    "\n",
    "    # 关键点：我们只需要传入新的消息，Context 会自动管理对话历史\n",
    "    completion = await context.completions.create(\n",
    "        [{\"role\": \"user\", \"content\": second_message}], stream=True\n",
    "    )\n",
    "\n",
    "    print(f\"👤 用户: {second_message}\")\n",
    "    print(\"🤖 AI: \", end=\"\")\n",
    "\n",
    "    async for chunk in completion:\n",
    "        if chunk.choices and chunk.choices[0].delta.content:\n",
    "            print(chunk.choices[0].delta.content, end=\"\")\n",
    "\n",
    "    print(\"\\n\" + \"=\" * 50)\n",
    "\n",
    "    # 第三轮对话：测试更深层的记忆\n",
    "    print(\"\\n🗣️ 第三轮对话...\")\n",
    "    third_message = \"在第一轮对话中我提出了什么请求？\"\n",
    "\n",
    "    completion = await context.completions.create(\n",
    "        [{\"role\": \"user\", \"content\": third_message}], stream=True\n",
    "    )\n",
    "\n",
    "    print(f\"👤 用户: {third_message}\")\n",
    "    print(\"🤖 AI: \", end=\"\")\n",
    "\n",
    "    async for chunk in completion:\n",
    "        if chunk.choices and chunk.choices[0].delta.content:\n",
    "            print(chunk.choices[0].delta.content, end=\"\")\n",
    "\n",
    "    print(\"\\n\" + \"=\" * 50)\n",
    "\n",
    "\n",
    "# 继续对话，展示记忆能力\n",
    "await demonstrate_memory(context)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "70c0e88f",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "📋 Context 内部存储的消息历史：\n",
      "总共有 6 条消息\n",
      "\n",
      "1. [USER]: 我是一个会飞的西兰花，和我打个招呼吧？\n",
      "2. [ASSISTANT]: 哇哦，会飞的西兰花你好呀！感觉你好特别呀，快给我讲讲你飞起来的奇妙体验呗~\n",
      "3. [USER]: 我在第一轮对话说我的身份是什么样的？\n",
      "4. [ASSISTANT]: 在第一轮对话中你说自己是“一个会飞的西兰花”呀。\n",
      "5. [USER]: 在第一轮对话中我提出了什么请求？\n",
      "6. [ASSISTANT]: 在第一轮对话中你提出的请求是“和我打个招呼吧？”\n",
      "\n",
      "💡 可以看到，Context 自动保存了所有的用户消息和 AI 回复！\n"
     ]
    }
   ],
   "source": [
    "# 检查 Context 内部的消息历史\n",
    "print(\"📋 Context 内部存储的消息历史：\")\n",
    "print(f\"总共有 {len(context.state.messages)} 条消息\")\n",
    "print()\n",
    "\n",
    "for i, message in enumerate(context.state.messages, 1):\n",
    "    role = message.get(\"role\", \"unknown\")\n",
    "    content = message.get(\"content\", \"\")\n",
    "\n",
    "    # 确保 content 是字符串类型\n",
    "    if isinstance(content, str):\n",
    "        # 截断过长的内容以便于显示\n",
    "        if len(content) > 100:\n",
    "            content = content[:100] + \"...\"\n",
    "    else:\n",
    "        content = str(content)\n",
    "\n",
    "    print(f\"{i}. [{role.upper()}]: {content}\")\n",
    "\n",
    "print(\"\\n💡 可以看到，Context 自动保存了所有的用户消息和 AI 回复！\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "31ab8abd",
   "metadata": {
    "vscode": {
     "languageId": "raw"
    }
   },
   "source": [
    "### 🔍 深入理解：为什么需要了解内部机制？\n",
    "\n",
    "在前面的示例中，我们看到了 Context 的强大功能。但作为工程师，仅仅知道\"如何使用\"是不够的，我们需要理解：\n",
    "\n",
    "1. **数据是如何流动的**：消息如何在系统中传递和存储\n",
    "2. **状态是如何管理的**：Context 如何跟踪对话历史\n",
    "3. **参数如何影响行为**：不同配置如何改变 AI 的响应方式\n",
    "\n",
    "接下来，我们将通过分析核心类来深入理解这些机制。这些知识将帮助您：\n",
    "- 更好地调试和优化您的 Agent\n",
    "- 理解复杂场景下的数据流向\n",
    "- 为后续章节的高级功能奠定基础\n",
    "\n",
    "---\n",
    "\n",
    "### 📨 ArkMessage：消息的数据结构\n",
    "\n",
    "每条消息在 Arkitect 中都以 `ArkMessage` 对象的形式存在。让我们深入了解这个核心数据结构。\n",
    "\n",
    "**核心字段解析：**\n",
    "\n",
    "```python\n",
    "class ArkMessage(BaseModel):\n",
    "    role: Literal[\"user\", \"system\", \"assistant\", \"tool\"]  # 消息角色\n",
    "    content: Optional[Union[str, List[...]]] = None       # 消息内容\n",
    "    tool_calls: Optional[List[...]] = None                # 工具调用信息\n",
    "    tool_call_id: Optional[str] = None                    # 工具调用ID\n",
    "    name: Optional[str] = None                            # 消息名称\n",
    "    reasoning_content: Optional[str] = None               # 推理内容\n",
    "```\n",
    "\n",
    "**关键理解：**\n",
    "- `role` 决定了消息的身份和处理方式（用户、系统、助手、工具）\n",
    "- `content` 是实际的消息内容，可以是文本或多媒体内容\n",
    "- `tool_calls` 和 `tool_call_id` 用于工具调用流程\n",
    "- 每种角色都有特定的验证规则和使用场景\n",
    "\n",
    "**学习 ArkMessage 的原因：**\n",
    "- 它是所有消息交互的基础数据结构\n",
    "- 理解它有助于调试和优化消息流\n",
    "- 为后续学习工具调用奠定基础\n",
    "\n",
    "**与其他概念的联系：**\n",
    "- State 管理着 ArkMessage 的集合\n",
    "- System Prompt 是特殊角色的 ArkMessage\n",
    "- 工具调用通过 ArkMessage 的特殊字段实现\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2501b62b",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "🔍 观察 Context 中存储的 ArkMessage 对象：\n",
      "总共有 6 条消息\n",
      "\n",
      "消息 1:\n",
      "  角色 (role): user\n",
      "  内容类型: <class 'str'>\n",
      "  内容预览: 我是一个会飞的西兰花，和我打个招呼吧？\n",
      "\n",
      "消息 2:\n",
      "  角色 (role): assistant\n",
      "  内容类型: <class 'str'>\n",
      "  内容预览: 哇哦，会飞的西兰花你好呀！感觉你好特别呀，快给我讲讲你飞起来的奇妙体验呗~\n",
      "\n",
      "消息 3:\n",
      "  角色 (role): user\n",
      "  内容类型: <class 'str'>\n",
      "  内容预览: 我在第一轮对话说我的身份是什么样的？\n",
      "\n",
      "消息 4:\n",
      "  角色 (role): assistant\n",
      "  内容类型: <class 'str'>\n",
      "  内容预览: 在第一轮对话中你说自己是“一个会飞的西兰花”呀。\n",
      "\n",
      "消息 5:\n",
      "  角色 (role): user\n",
      "  内容类型: <class 'str'>\n",
      "  内容预览: 在第一轮对话中我提出了什么请求？\n",
      "\n",
      "消息 6:\n",
      "  角色 (role): assistant\n",
      "  内容类型: <class 'str'>\n",
      "  内容预览: 在第一轮对话中你提出的请求是“和我打个招呼吧？”\n",
      "\n",
      "💡 可以看到，每条消息都是一个结构化的 ArkMessage 对象！\n",
      "🔄 数据流：用户输入字典 → 框架内部转换 → ArkMessage 对象 → 存储在 State 中\n"
     ]
    }
   ],
   "source": [
    "# 观察 Context 中的 ArkMessage 对象\n",
    "print(\"🔍 观察 Context 中存储的 ArkMessage 对象：\")\n",
    "print(f\"总共有 {len(context.state.messages)} 条消息\\n\")\n",
    "\n",
    "for i, message in enumerate(context.state.messages, 1):\n",
    "    print(f\"消息 {i}:\")\n",
    "    print(f\"  角色 (role): {message.get('role', 'unknown')}\")\n",
    "    print(f\"  内容类型: {type(message.get('content', ''))}\")\n",
    "\n",
    "    # 显示内容的前50个字符\n",
    "    content = message.get(\"content\", \"\")\n",
    "    if isinstance(content, str):\n",
    "        content_preview = content[:50] + \"...\" if len(content) > 50 else content\n",
    "        print(f\"  内容预览: {content_preview}\")\n",
    "    else:\n",
    "        print(f\"  内容: {content}\")\n",
    "\n",
    "    # 显示其他关键字段\n",
    "    tool_calls = message.get(\"tool_calls\")\n",
    "    if tool_calls:\n",
    "        try:\n",
    "            print(\n",
    "                f\"  工具调用: {\n",
    "                    len(tool_calls) if isinstance(tool_calls, list) else 0\n",
    "                } 个\"\n",
    "            )\n",
    "        except TypeError:\n",
    "            print(\"  工具调用: 存在\")\n",
    "    tool_call_id = message.get(\"tool_call_id\")\n",
    "    if tool_call_id:\n",
    "        print(f\"  工具调用ID: {tool_call_id}\")\n",
    "\n",
    "    print()\n",
    "\n",
    "print(\"💡 可以看到，每条消息都是一个结构化的 ArkMessage 对象！\")\n",
    "print(\"🔄 数据流：用户输入字典 → 框架内部转换 → ArkMessage 对象 → 存储在 State 中\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f50cfb17",
   "metadata": {
    "vscode": {
     "languageId": "raw"
    }
   },
   "source": [
    "### 🗂️ State：对话状态的管理者\n",
    "\n",
    "Context 的记忆能力来自于 `State` 类，它负责管理整个对话的状态信息。\n",
    "\n",
    "**核心字段解析：**\n",
    "\n",
    "```python\n",
    "class State(BaseModel):\n",
    "    messages: List[ArkMessage] = []           # 消息历史列表\n",
    "    parameters: ArkChatParameters = ...       # 聊天参数配置\n",
    "    tools: Optional[List[ChatCompletionTool]] = None  # 可用工具列表\n",
    "    context_id: Optional[str] = None          # 上下文ID\n",
    "    # ... 其他状态字段\n",
    "```\n",
    "\n",
    "**关键理解：**\n",
    "- `messages` 存储了完整的对话历史\n",
    "- `parameters` 控制 AI 的行为方式（温度、最大token等）\n",
    "- `tools` 定义了 Agent 可以使用的工具\n",
    "- State 提供了状态的持久化和恢复能力\n",
    "\n",
    "**与 ArkMessage 的联系：**\n",
    "- State 管理着 ArkMessage 的集合\n",
    "- 每次对话都会向 State.messages 添加新的 ArkMessage\n",
    "- State 确保消息的顺序和完整性\n",
    "\n",
    "**学习 State 的原因：**\n",
    "- 理解对话状态的存储和管理机制\n",
    "- 掌握如何监控和调试对话状态\n",
    "- 为高级功能（如状态恢复、参数调整）奠定基础\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "2ccbf74f",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "🗂️ 观察 Context 的 State 对象：\n",
      "State 类型: <class 'arkitect.core.component.context.model.State'>\n",
      "消息数量: 6\n"
     ]
    }
   ],
   "source": [
    "# 观察 Context 的 State 对象\n",
    "print(\"🗂️ 观察 Context 的 State 对象：\")\n",
    "print(f\"State 类型: {type(context.state)}\")\n",
    "print(f\"消息数量: {len(context.state.messages)}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c5855528",
   "metadata": {
    "vscode": {
     "languageId": "raw"
    }
   },
   "source": [
    "### 深入理解：State 类\n",
    "\n",
    "现在让我们来了解 Context 的另一个核心组件：**State 类**。\n",
    "\n",
    "State 是 Context 的状态容器，它负责存储和管理整个对话会话的所有信息。可以把 State 想象成 Context 的\"大脑\"，它记录着：\n",
    "\n",
    "- 📝 **消息历史**：所有的对话记录\n",
    "- ⚙️ **配置信息**：模型参数、工具配置等\n",
    "- 🔄 **会话状态**：当前对话的状态信息\n",
    "- 🛠️ **工具调用**：工具的调用历史和结果\n",
    "\n",
    "让我们深入探索 State 的结构：\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "4f02d76d",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "🧠 State 类深入解析\n",
      "==================================================\n",
      "\n",
      "📊 Context 的 State 对象：\n",
      "   类型: State\n",
      "   State 对象: context_id='' messages=[{'role': 'user', 'content': '我是一个会飞的西兰花，和我打个招呼吧？'}, {'content': '哇哦，会飞的西兰花你好呀！感觉你好特别呀，快给我讲讲你飞起来的奇妙体验呗~', 'role': 'assistant', 'function_call': None, 'tool_calls': [], 'audio': None, 'reasoning_content': None}, {'role': 'user', 'content': '我在第一轮对话说我的身份是什么样的？'}, {'content': '在第一轮对话中你说自己是“一个会飞的西兰花”呀。', 'role': 'assistant', 'function_call': None, 'tool_calls': [], 'audio': None, 'reasoning_content': None}, {'role': 'user', 'content': '在第一轮对话中我提出了什么请求？'}, {'content': '在第一轮对话中你提出的请求是“和我打个招呼吧？”', 'role': 'assistant', 'function_call': None, 'tool_calls': [], 'audio': None, 'reasoning_content': None}] parameters=None context_parameters=None details=None\n",
      "\n",
      "🔍 State 的核心属性：\n",
      "   messages: 包含 6 条消息\n",
      "   parameters: 使用默认参数\n",
      "\n",
      "📝 State.messages 详细信息：\n",
      "   存储类型: <class 'list'>\n",
      "   消息数量: 6\n",
      "   第一条消息类型: <class 'dict'>\n",
      "   最后一条消息类型: <class 'dict'>\n",
      "\n",
      "🔄 State 的作用机制：\n",
      "1. 自动更新: 每次对话后 State 自动更新\n",
      "2. 历史管理: 维护完整的对话历史\n",
      "3. 状态持久化: 保存会话的所有状态信息\n",
      "4. 配置存储: 存储模型和工具配置\n"
     ]
    }
   ],
   "source": [
    "print(\"🧠 State 类深入解析\")\n",
    "print(\"=\" * 50)\n",
    "\n",
    "# 1. 展示 State 的基本信息\n",
    "print(\"\\n📊 Context 的 State 对象：\")\n",
    "print(f\"   类型: {type(context.state).__name__}\")\n",
    "print(f\"   State 对象: {context.state}\")\n",
    "\n",
    "# 2. 展示 State 的核心属性\n",
    "print(\"\\n🔍 State 的核心属性：\")\n",
    "state_attrs = dir(context.state)\n",
    "important_attrs = [\"messages\", \"model\", \"tools\", \"parameters\"]\n",
    "\n",
    "for attr in important_attrs:\n",
    "    if hasattr(context.state, attr):\n",
    "        value = getattr(context.state, attr)\n",
    "        if attr == \"messages\":\n",
    "            print(f\"   {attr}: 包含 {len(value)} 条消息\")\n",
    "        elif attr == \"tools\":\n",
    "            print(f\"   {attr}: {value if value else '无工具'}\")\n",
    "        elif attr == \"model\":\n",
    "            print(f\"   {attr}: {value}\")\n",
    "        elif attr == \"parameters\":\n",
    "            print(f\"   {attr}: {value if value else '使用默认参数'}\")\n",
    "        else:\n",
    "            print(f\"   {attr}: {value}\")\n",
    "\n",
    "# 3. 深入查看 messages 属性\n",
    "print(\"\\n📝 State.messages 详细信息：\")\n",
    "print(f\"   存储类型: {type(context.state.messages)}\")\n",
    "print(f\"   消息数量: {len(context.state.messages)}\")\n",
    "\n",
    "if context.state.messages:\n",
    "    print(f\"   第一条消息类型: {type(context.state.messages[0])}\")\n",
    "    print(f\"   最后一条消息类型: {type(context.state.messages[-1])}\")\n",
    "\n",
    "print(\"\\n🔄 State 的作用机制：\")\n",
    "print(\"1. 自动更新: 每次对话后 State 自动更新\")\n",
    "print(\"2. 历史管理: 维护完整的对话历史\")\n",
    "print(\"3. 状态持久化: 保存会话的所有状态信息\")\n",
    "print(\"4. 配置存储: 存储模型和工具配置\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "63deb471",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "🔄 演示 State 的实时更新机制\n",
      "==================================================\n",
      "\n",
      "📊 发送新消息前的状态：\n",
      "   当前消息数量: 6\n",
      "\n",
      "🗣️ 发送新消息...\n",
      "👤 用户: 请简单介绍一下你自己\n",
      "🤖 AI: 我是豆包呀，我可以陪你聊天、为你解答各类问题，不管是生活小常识、学习上的疑惑，还是你想聊的各种有趣话题，我都随时准备为你服务哟。\n",
      "==============================\n",
      "\n",
      "📊 发送新消息后的状态：\n",
      "   更新后消息数量: 8\n",
      "   新增消息数量: 2\n",
      "\n",
      "📝 最新的消息：\n",
      "   7. [USER]: 请简单介绍一下你自己\n",
      "   8. [ASSISTANT]: 我是豆包呀，我可以陪你聊天、为你解答各类问题，不管是生活小常识、学习上的疑惑，还是你想聊的各种有趣话...\n",
      "\n",
      "✨ 可以看到，State 自动跟踪并更新了所有的对话状态！\n",
      "🔗 Context 和 State 的关系：Context 使用 State 来管理所有的会话数据\n"
     ]
    }
   ],
   "source": [
    "# 4. 演示 State 的实时更新机制\n",
    "print(\"🔄 演示 State 的实时更新机制\")\n",
    "print(\"=\" * 50)\n",
    "\n",
    "# 记录当前状态\n",
    "current_message_count = len(context.state.messages)\n",
    "print(\"\\n📊 发送新消息前的状态：\")\n",
    "print(f\"   当前消息数量: {current_message_count}\")\n",
    "\n",
    "# 发送一条新消息\n",
    "print(\"\\n🗣️ 发送新消息...\")\n",
    "test_message = \"请简单介绍一下你自己\"\n",
    "\n",
    "completion = await context.completions.create(\n",
    "    [{\"role\": \"user\", \"content\": test_message}], stream=True\n",
    ")\n",
    "\n",
    "print(f\"👤 用户: {test_message}\")\n",
    "print(\"🤖 AI: \", end=\"\")\n",
    "\n",
    "async for chunk in completion:\n",
    "    if chunk.choices and chunk.choices[0].delta.content:\n",
    "        print(chunk.choices[0].delta.content, end=\"\")\n",
    "\n",
    "print(\"\\n\" + \"=\" * 30)\n",
    "\n",
    "# 检查更新后的状态\n",
    "new_message_count = len(context.state.messages)\n",
    "print(\"\\n📊 发送新消息后的状态：\")\n",
    "print(f\"   更新后消息数量: {new_message_count}\")\n",
    "print(f\"   新增消息数量: {new_message_count - current_message_count}\")\n",
    "\n",
    "# 显示最新的消息\n",
    "if context.state.messages:\n",
    "    latest_messages = context.state.messages[-2:]  # 最后两条消息\n",
    "    print(\"\\n📝 最新的消息：\")\n",
    "    for i, msg in enumerate(latest_messages, len(context.state.messages) - 1):\n",
    "        role = msg.get(\"role\", \"unknown\")\n",
    "        content = msg.get(\"content\", \"\")\n",
    "\n",
    "        # 安全地处理content，确保它是字符串\n",
    "        if isinstance(content, str):\n",
    "            if len(content) > 50:\n",
    "                content = content[:50] + \"...\"\n",
    "        else:\n",
    "            content = str(content) if content else \"\"\n",
    "\n",
    "        print(f\"   {i}. [{role.upper()}]: {content}\")\n",
    "\n",
    "print(\"\\n✨ 可以看到，State 自动跟踪并更新了所有的对话状态！\")\n",
    "print(\"🔗 Context 和 State 的关系：Context 使用 State 来管理所有的会话数据\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f62ba226",
   "metadata": {
    "vscode": {
     "languageId": "raw"
    }
   },
   "source": [
    "### System Prompt 的威力\n",
    "\n",
    "现在让我们学习一个非常重要的概念：**System Prompt（系统提示）**。\n",
    "\n",
    "System Prompt 是一种特殊的 `ArkMessage`，它的 `role` 设置为 `\"system\"`。System Prompt 的作用是：\n",
    "\n",
    "- 🎭 **定义 AI 的角色和性格**：告诉 AI 它应该扮演什么角色\n",
    "- 📋 **设置行为准则**：指定 AI 应该如何回应用户\n",
    "- 🎯 **提供背景信息**：给 AI 提供必要的上下文信息\n",
    "- 🔧 **控制输出格式**：指定回复的格式和风格\n",
    "\n",
    "**重要提示**：System Prompt 本质上是消息流的一部分，而不是 API 参数。它通过 `completions.create()` 方法与其他消息一起发送。\n",
    "\n",
    "让我们通过实际例子来看看 System Prompt 的强大功能：\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9dde5366",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "🎭 System Prompt 功能演示\n",
      "==================================================\n",
      "\n",
      "🔍 场景 1：没有 System Prompt\n",
      "------------------------------\n",
      "👤 用户: 今天天气怎么样？\n",
      "🤖 普通 AI: 你可以通过以下几种方式获取今天的天气：\n",
      "### 1. 手机天气应用\n",
      "打开手机自带的天气应用程序，一般授权定位后就能直接看到当前所在地的实时天气。\n",
      "### 2. 搜索引擎查询\n",
      "在百度、微信等搜索引擎中输入“[你所在城市]今天天气”，比如“北京今天天气”，就能获取到当地的实时天气信息。\n",
      "### 3. 智能音箱查询\n",
      "如果家里有智能音箱，比如小爱同学、小度等，直接对音箱说“查询[你所在城市]今天天气”，音箱会播报天气情况。\n",
      "==================================================\n",
      "\n",
      "🎭 场景 2：使用 System Prompt（海盗角色）\n",
      "------------------------------\n",
      "👤 用户: 今天天气怎么样？\n",
      "🏴‍☠️ 海盗 AI: 哟，宝贝儿！今儿个这天气嘛，往那海洋上一瞄，太阳明晃晃地烤着，风轻轻儿地吹着，咱海盗可不在乎这些个，只惦记着能抢到大把的财宝呢，哈哈！\n",
      "==================================================\n"
     ]
    }
   ],
   "source": [
    "async def demonstrate_system_prompt():\n",
    "    \"\"\"演示 System Prompt 的强大功能\"\"\"\n",
    "\n",
    "    print(\"🎭 System Prompt 功能演示\")\n",
    "    print(\"=\" * 50)\n",
    "\n",
    "    # 创建一个新的 Context 用于演示\n",
    "    MODEL = \"doubao-seed-1-6-flash-250615\"\n",
    "    demo_context = Context(model=MODEL)\n",
    "    await demo_context.init()\n",
    "\n",
    "    # 测试问题\n",
    "    test_question = \"今天天气怎么样？\"\n",
    "\n",
    "    # 1. 没有 System Prompt 的对话\n",
    "    print(\"\\n🔍 场景 1：没有 System Prompt\")\n",
    "    print(\"-\" * 30)\n",
    "\n",
    "    completion = await demo_context.completions.create(\n",
    "        [{\"role\": \"user\", \"content\": test_question}], stream=True\n",
    "    )\n",
    "\n",
    "    print(f\"👤 用户: {test_question}\")\n",
    "    print(\"🤖 普通 AI: \", end=\"\")\n",
    "\n",
    "    async for chunk in completion:\n",
    "        if chunk.choices and chunk.choices[0].delta.content:\n",
    "            print(chunk.choices[0].delta.content, end=\"\")\n",
    "\n",
    "    print(\"\\n\" + \"=\" * 50)\n",
    "\n",
    "    # 2. 使用 System Prompt 的对话\n",
    "    print(\"\\n🎭 场景 2：使用 System Prompt（海盗角色）\")\n",
    "    print(\"-\" * 30)\n",
    "\n",
    "    # 创建另一个 Context 用于对比\n",
    "    pirate_context = Context(model=MODEL)\n",
    "    await pirate_context.init()\n",
    "\n",
    "    # 使用 System Prompt 定义海盗角色\n",
    "    completion = await pirate_context.completions.create(\n",
    "        [\n",
    "            {\n",
    "                \"role\": \"system\",\n",
    "                \"content\": (\n",
    "                    \"你是一个古老的海盗船长，\"\n",
    "                    \"说话要用海盗的语调和词汇。每句话都要带有海盗的特色，\"\n",
    "                    \"比如'船长'、'宝贝'、'海洋'等词汇。\"\n",
    "                ),\n",
    "            },\n",
    "            {\"role\": \"user\", \"content\": test_question},\n",
    "        ],\n",
    "        stream=True,\n",
    "    )\n",
    "\n",
    "    print(f\"👤 用户: {test_question}\")\n",
    "    print(\"🏴‍☠️ 海盗 AI: \", end=\"\")\n",
    "\n",
    "    async for chunk in completion:\n",
    "        if chunk.choices and chunk.choices[0].delta.content:\n",
    "            print(chunk.choices[0].delta.content, end=\"\")\n",
    "\n",
    "    print(\"\\n\" + \"=\" * 50)\n",
    "\n",
    "    return demo_context, pirate_context\n",
    "\n",
    "\n",
    "# 运行演示\n",
    "demo_context, pirate_context = await demonstrate_system_prompt()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4356e4cb",
   "metadata": {
    "vscode": {
     "languageId": "raw"
    }
   },
   "source": [
    "### 🎯 第 1 章总结\n",
    "\n",
    "通过本章的学习，您已经掌握了 Arkitect 框架的核心概念：\n",
    "\n",
    "#### 1. **Context（对话管理器）**\n",
    "- ✅ Context 是框架的核心组件，管理整个对话会话\n",
    "- ✅ 自动处理多轮对话，无需手动管理消息历史\n",
    "- ✅ 通过 `completions.create()` 方法与 LLM 交互\n",
    "- ✅ 支持流式响应，提供实时的用户体验\n",
    "\n",
    "#### 2. **ArkMessage（消息对象）**\n",
    "- ✅ 理解了消息的内部结构和不同角色类型\n",
    "- ✅ 掌握了字典到 ArkMessage 对象的转换机制\n",
    "- ✅ 学会了如何检查和分析消息对象\n",
    "\n",
    "#### 3. **State（状态管理）**\n",
    "- ✅ 了解了 State 作为 Context 状态容器的作用\n",
    "- ✅ 掌握了 State 的自动更新机制\n",
    "- ✅ 学会了如何检查和监控对话状态\n",
    "\n",
    "#### 4. **System Prompt（系统提示）**\n",
    "- ✅ 理解了 System Prompt 的本质和作用\n",
    "- ✅ 掌握了如何使用 System Prompt 定制 AI 行为\n",
    "- ✅ 学会了 System Prompt 在消息流中的位置和存储\n",
    "\n",
    "#### 🔑 **关键要点回顾**\n",
    "1. **Context = 对话管理器**：自动处理所有对话状态\n",
    "2. **ArkMessage = 消息单位**：统一的消息表示格式\n",
    "3. **State = 状态容器**：存储所有会话数据\n",
    "4. **System Prompt = 行为定制**：通过消息流控制 AI 行为\n",
    "\n",
    "#### 🚀 **下一步**\n",
    "在下一章中，我们将学习如何为 Context 添加工具，构建能够执行实际操作的 Agent！\n",
    "\n",
    "---\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7c5be5bb",
   "metadata": {
    "vscode": {
     "languageId": "raw"
    }
   },
   "source": [
    "## 第 2 章：构建您的第一个 Agent\n",
    "\n",
    "在第一章中，我们学会了如何创建 Context 并进行基本的对话。但是，一个真正有用的 AI Agent 需要能够执行实际的操作，而不仅仅是聊天。\n",
    "\n",
    "### 什么是 Agent？\n",
    "\n",
    "**Agent** 是一个配备了特定工具和能力的 AI 系统，它能够：\n",
    "\n",
    "- 🧠 **理解用户意图**：准确理解用户的请求和需求\n",
    "- 🛠️ **选择合适工具**：根据任务自动选择最适合的工具\n",
    "- 🔄 **执行实际操作**：调用工具完成具体任务\n",
    "- 📊 **整合结果**：将工具执行结果融入自然的对话回复中\n",
    "\n",
    "### 工具系统的核心概念\n",
    "\n",
    "在 Arkitect 中，工具系统让您能够：\n",
    "\n",
    "1. **将 Python 函数转换为 AI 可调用的工具**\n",
    "2. **让 AI 自动选择和调用合适的工具**\n",
    "3. **处理工具调用的参数验证和结果返回**\n",
    "\n",
    "让我们通过构建一个天气查询 Agent 来学习这些概念！\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4d04f0fc",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "🛠️ 天气查询工具函数已定义\n"
     ]
    }
   ],
   "source": [
    "# 定义工具函数\n",
    "def get_weather(city: str) -> str:\n",
    "    \"\"\"\n",
    "    获取指定城市的天气信息\n",
    "\n",
    "    Args:\n",
    "        city: 城市名称，例如 \"北京\", \"上海\", \"广州\"\n",
    "\n",
    "    Returns:\n",
    "        天气信息的字符串描述\n",
    "    \"\"\"\n",
    "    # 这里我们模拟天气查询，实际应用中您可以调用真实的天气API\n",
    "    weather_data = {\n",
    "        \"北京\": \"晴天，气温 25°C，微风\",\n",
    "        \"上海\": \"多云，气温 28°C，东南风\",\n",
    "        \"广州\": \"雷阵雨，气温 30°C，南风\",\n",
    "        \"深圳\": \"晴天，气温 32°C，微风\",\n",
    "        \"杭州\": \"阴天，气温 26°C，西北风\",\n",
    "    }\n",
    "\n",
    "    # 查找城市天气\n",
    "    if city in weather_data:\n",
    "        return f\"{city}今天的天气：{weather_data[city]}\"\n",
    "    else:\n",
    "        return f\"抱歉，暂时无法获取{city}的天气信息。目前支持的城市有：{\n",
    "            ', '.join(weather_data.keys())\n",
    "        }\"\n",
    "\n",
    "\n",
    "print(\"🛠️ 天气查询工具函数已定义\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "558a30c0",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "🧪 测试天气查询工具函数：\n",
      "========================================\n",
      "📍 北京: 北京今天的天气：晴天，气温 25°C，微风\n",
      "📍 上海: 上海今天的天气：多云，气温 28°C，东南风\n",
      "📍 成都: 抱歉，暂时无法获取成都的天气信息。目前支持的城市有：北京, 上海, 广州, 深圳, 杭州\n",
      "\n",
      "✅ 工具函数测试完成！接下来我们将把它转换为 AI 可调用的工具。\n"
     ]
    }
   ],
   "source": [
    "# 测试工具函数\n",
    "print(\"🧪 测试天气查询工具函数：\")\n",
    "print(\"=\" * 40)\n",
    "\n",
    "# 测试几个城市\n",
    "test_cities = [\"北京\", \"上海\", \"成都\"]\n",
    "\n",
    "for city in test_cities:\n",
    "    result = get_weather(city)\n",
    "    print(f\"📍 {city}: {result}\")\n",
    "\n",
    "print(\"\\n✅ 工具函数测试完成！接下来我们将把它转换为 AI 可调用的工具。\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "cb15cd34",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "🌤️ 创建天气查询 Agent...\n",
      "==================================================\n",
      "\n",
      "Context 已创建，使用模型: doubao-seed-1-6-flash-250615\n",
      "\n",
      "✅ 天气查询 Agent 创建完成！\n"
     ]
    }
   ],
   "source": [
    "async def create_weather_agent():\n",
    "    \"\"\"创建一个天气查询 Agent\"\"\"\n",
    "\n",
    "    print(\"🌤️ 创建天气查询 Agent...\")\n",
    "    print(\"=\" * 50)\n",
    "\n",
    "    # 创建 Context 并配置工具和参数\n",
    "    MODEL = \"doubao-seed-1-6-flash-250615\"\n",
    "    weather_context = Context(\n",
    "        model=MODEL,\n",
    "        tools=[get_weather],  # 直接传递函数, 而非在parameters里注册\n",
    "    )\n",
    "    await weather_context.init()\n",
    "\n",
    "    print(f\"\\nContext 已创建，使用模型: {MODEL}\")\n",
    "\n",
    "    print(\"\\n✅ 天气查询 Agent 创建完成！\")\n",
    "    return weather_context\n",
    "\n",
    "\n",
    "# 创建 Agent\n",
    "weather_context = await create_weather_agent()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "46097125",
   "metadata": {
    "vscode": {
     "languageId": "raw"
    }
   },
   "source": [
    "### 🛠️ ChatCompletionTool：工具系统的核心\n",
    "\n",
    "Agent 能够执行实际操作的能力来自于 `ChatCompletionTool` 类，它是 Arkitect 工具系统的核心组件。\n",
    "\n",
    "**核心字段解析：**\n",
    "\n",
    "```python\n",
    "class ChatCompletionTool(BaseModel):\n",
    "    type: Literal[\"function\"] = \"function\"     # 工具类型\n",
    "    function: FunctionDefinition               # 函数定义和元数据\n",
    "```\n",
    "\n",
    "**关键理解：**\n",
    "- `type` 标识工具的类型（目前主要是 \"function\"）\n",
    "- `function` 包含了工具的完整定义，包括名称、描述、参数结构\n",
    "- ChatCompletionTool 是 Python 函数与 LLM 之间的桥梁\n",
    "- 通过 `from_function()` 方法可以自动从 Python 函数生成工具定义\n",
    "\n",
    "**学习 ChatCompletionTool 的原因：**\n",
    "- 它是所有工具调用的基础数据结构\n",
    "- 理解它有助于调试工具调用问题\n",
    "- 为后续学习工具调用流程奠定基础\n",
    "\n",
    "**与其他概念的联系：**\n",
    "- State 的 `tools` 字段存储 ChatCompletionTool 列表\n",
    "- ArkMessage 的 `tool_calls` 字段引用这些工具\n",
    "- 工具调用结果也会以 ArkMessage 的形式存储\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0848730c",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "🔧 演示 ChatCompletionTool.from_function() 方法：\n",
      "==================================================\n",
      "📊 工具转换结果：\n",
      "   工具类型: ChatCompletionTool\n",
      "   工具名称: get_weather\n",
      "   工具描述: \n",
      "    获取指定城市的天气信息\n",
      "\n",
      "    Args:\n",
      "        city: 城市名称，例如 \"北京\", \"上海\", \"广州\"\n",
      "\n",
      "    Returns:\n",
      "        天气信息的字符串描述\n",
      "    \n",
      "   工具类型标识: function\n",
      "\n",
      "🔍 工具的完整结构：\n",
      "   Function 对象: name='get_weather' description='\\n    获取指定城市的天气信息\\n\\n    Args:\\n        city: 城市名称，例如 \"北京\", \"上海\", \"广州\"\\n\\n    Returns:\\n        天气信息的字符串描述\\n    ' parameters={'properties': {'city': {'default': None, 'type': 'string'}}, 'type': 'object', 'required': ['city']}\n",
      "   Function 类型: <class 'arkitect.types.llm.model.FunctionDefinition'>\n",
      "\n",
      "✨ 可以看到，from_function() 自动从函数签名和文档字符串生成了完整的工具描述！\n"
     ]
    }
   ],
   "source": [
    "# 演示 from_function() 方法\n",
    "print(\"\\n🔧 演示 ChatCompletionTool.from_function() 方法：\")\n",
    "print(\"=\" * 50)\n",
    "\n",
    "# 重新创建工具以观察转换过程\n",
    "weather_tool = ChatCompletionTool.from_function(get_weather)\n",
    "\n",
    "print(\"📊 工具转换结果：\")\n",
    "print(f\"   工具类型: {type(weather_tool).__name__}\")\n",
    "print(f\"   工具名称: {weather_tool.function.name}\")\n",
    "print(f\"   工具描述: {weather_tool.function.description}\")\n",
    "print(f\"   工具类型标识: {weather_tool.type}\")\n",
    "\n",
    "print(\"\\n🔍 工具的完整结构：\")\n",
    "print(f\"   Function 对象: {weather_tool.function}\")\n",
    "print(f\"   Function 类型: {type(weather_tool.function)}\")\n",
    "\n",
    "print(\"\\n✨ 可以看到，from_function() 自动从函数签名和文档字符串生成了完整的工具描述！\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "7e08f9c2",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "📋 检查工具的 JSON Schema：\n",
      "========================================\n",
      "🔍 工具参数的 JSON Schema：\n",
      "   Schema 类型: <class 'dict'>\n",
      "   Schema 内容: {'properties': {'city': {'default': None, 'type': 'string'}}, 'type': 'object', 'required': ['city']}\n",
      "\n",
      "📝 参数详细信息：\n",
      "   - city (string): 无描述\n",
      "\n",
      "💡 这个 JSON Schema 就是 LLM 理解和调用工具的依据！\n"
     ]
    }
   ],
   "source": [
    "# 检查工具的 JSON Schema\n",
    "print(\"📋 检查工具的 JSON Schema：\")\n",
    "print(\"=\" * 40)\n",
    "\n",
    "print(\"🔍 工具参数的 JSON Schema：\")\n",
    "parameters = weather_tool.function.parameters\n",
    "print(f\"   Schema 类型: {type(parameters)}\")\n",
    "print(f\"   Schema 内容: {parameters}\")\n",
    "\n",
    "if \"properties\" in parameters:\n",
    "    print(\"\\n📝 参数详细信息：\")\n",
    "    for param_name, param_info in parameters[\"properties\"].items():\n",
    "        param_type = param_info.get(\"type\", \"unknown\")\n",
    "        param_desc = param_info.get(\"description\", \"无描述\")\n",
    "        print(f\"   - {param_name} ({param_type}): {param_desc}\")\n",
    "\n",
    "print(\"\\n💡 这个 JSON Schema 就是 LLM 理解和调用工具的依据！\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "3d793a88",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "🌤️ 与天气 Agent 交互测试\n",
      "==================================================\n",
      "\n",
      "🧪 测试 1: 北京今天天气怎么样？\n",
      "------------------------------\n",
      "👤 用户: 北京今天天气怎么样？\n",
      "🤖 Agent: 北京今天的天气：晴天，气温 25°C，微风\n",
      "==============================\n",
      "\n",
      "🧪 测试 2: 请查询上海的天气\n",
      "------------------------------\n",
      "👤 用户: 请查询上海的天气\n",
      "🤖 Agent: 上海今天的天气：多云，气温 28°C，东南风\n",
      "==============================\n",
      "\n",
      "🧪 测试 3: 广州现在是什么天气？\n",
      "------------------------------\n",
      "👤 用户: 广州现在是什么天气？\n",
      "🤖 Agent: 广州今天的天气：雷阵雨，气温 30°C，南风\n",
      "==============================\n"
     ]
    }
   ],
   "source": [
    "async def test_weather_agent():\n",
    "    \"\"\"与天气 Agent 交互\"\"\"\n",
    "\n",
    "    print(\"🌤️ 与天气 Agent 交互测试\")\n",
    "    print(\"=\" * 50)\n",
    "\n",
    "    # 测试问题列表\n",
    "    test_questions = [\n",
    "        \"北京今天天气怎么样？\",\n",
    "        \"请查询上海的天气\",\n",
    "        \"广州现在是什么天气？\",\n",
    "    ]\n",
    "\n",
    "    for i, question in enumerate(test_questions, 1):\n",
    "        print(f\"\\n🧪 测试 {i}: {question}\")\n",
    "        print(\"-\" * 30)\n",
    "\n",
    "        # 发送请求（工具已在 Context 创建时配置）\n",
    "        completion = await weather_context.completions.create(\n",
    "            [{\"role\": \"user\", \"content\": question}], stream=True\n",
    "        )\n",
    "\n",
    "        print(f\"👤 用户: {question}\")\n",
    "        print(\"🤖 Agent: \", end=\"\")\n",
    "\n",
    "        async for chunk in completion:\n",
    "            if hasattr(chunk, \"choices\") and chunk.choices:\n",
    "                delta = chunk.choices[0].delta\n",
    "                if hasattr(delta, \"content\") and delta.content:\n",
    "                    print(delta.content, end=\"\")\n",
    "\n",
    "        print(\"\\n\" + \"=\" * 30)\n",
    "\n",
    "\n",
    "# 运行测试\n",
    "await test_weather_agent()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a4ed22c1",
   "metadata": {
    "vscode": {
     "languageId": "raw"
    }
   },
   "source": [
    "### 🔄 ToolChunk：流式响应中的工具调用\n",
    "\n",
    "在流式响应中，工具调用是通过 `ToolChunk` 类来处理的。让我们深入了解这个类。\n",
    "\n",
    "**核心字段解析：**\n",
    "\n",
    "```python\n",
    "class ToolChunk(BaseModel):\n",
    "    type: Literal[\"tool_call\"] = \"tool_call\"   # 块类型\n",
    "    id: str                                    # 工具调用ID\n",
    "    function: dict                             # 函数调用信息\n",
    "```\n",
    "\n",
    "**关键理解：**\n",
    "- `type` 标识这是一个工具调用块\n",
    "- `id` 是工具调用的唯一标识符\n",
    "- `function` 包含函数名和参数信息\n",
    "- ToolChunk 在流式响应中用于处理工具调用\n",
    "\n",
    "**学习 ToolChunk 的原因：**\n",
    "- 理解流式响应中工具调用的处理机制\n",
    "- 掌握如何在流式输出中识别和处理工具调用\n",
    "- 为高级工具调用调试奠定基础\n",
    "\n",
    "**与其他概念的联系：**\n",
    "- ToolChunk 是流式响应中的特殊数据块\n",
    "- 最终会转换为 ArkMessage 中的 tool_calls 字段\n",
    "- 与 ChatCompletionTool 配合实现完整的工具调用流程\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4611a100",
   "metadata": {},
   "outputs": [],
   "source": [
    "async def demonstrate_tool_chunks():\n",
    "    \"\"\"演示流式响应中的工具调用\"\"\"\n",
    "\n",
    "    print(\"🔄 演示流式响应中的工具调用\")\n",
    "    print(\"=\" * 50)\n",
    "\n",
    "    question = \"深圳的天气如何？\"\n",
    "\n",
    "    print(f\"👤 用户: {question}\")\n",
    "    print()\n",
    "    print(\"🤖 Agent 响应过程:\")\n",
    "    print(\"   [分析用户请求...]\")\n",
    "\n",
    "    completion = await weather_context.completions.create(\n",
    "        [{\"role\": \"user\", \"content\": question}], stream=True\n",
    "    )\n",
    "\n",
    "    tool_calls_detected = []\n",
    "    response_content = \"\"\n",
    "\n",
    "    async for chunk in completion:\n",
    "        # 检查是否是 ToolChunk\n",
    "        if hasattr(chunk, \"tool_name\"):\n",
    "            tool_calls_detected.append(chunk)\n",
    "            print(f\"   [选择工具: {chunk.tool_name}]\")\n",
    "            print(f\"   [调用工具: {chunk.tool_name}({chunk.tool_arguments})]\")\n",
    "            print(f\"   [工具返回: {chunk.tool_response}]\")\n",
    "            print(\"   [生成回复...]\")\n",
    "        elif hasattr(chunk, \"choices\") and chunk.choices:\n",
    "            delta = chunk.choices[0].delta\n",
    "            if hasattr(delta, \"content\") and delta.content:\n",
    "                response_content += delta.content\n",
    "                print(delta.content, end=\"\")\n",
    "\n",
    "    print(\"\\n\")\n",
    "    print(\"=\" * 50)\n",
    "\n",
    "    # 分析结果\n",
    "    print(\"📊 工具调用分析:\")\n",
    "    if tool_calls_detected:\n",
    "        print(f\"✅ 检测到 {len(tool_calls_detected)} 次工具调用\")\n",
    "        for i, tool_chunk in enumerate(tool_calls_detected, 1):\n",
    "            print(f\"   🔧 工具名称: {tool_chunk.tool_name}\")\n",
    "            print(f\"   📝 调用参数: {tool_chunk.tool_arguments}\")\n",
    "            print(f\"   ✨ 返回结果: {tool_chunk.tool_response}\")\n",
    "            if tool_chunk.tool_exception:\n",
    "                print(f\"   ❌ 异常信息: {tool_chunk.tool_exception}\")\n",
    "    else:\n",
    "        print(\"❌ 未检测到工具调用\")\n",
    "        print(\"   可能原因: 1) 问题不需要工具 2) 工具配置有误 3) 模型未选择工具\")\n",
    "\n",
    "    print()\n",
    "    print(\n",
    "        \"💡 可以看到，Agent 成功识别了用户的天气查询需求，自动选择并调用了合适的工具！\"\n",
    "    )\n",
    "    print(\"🔍 这展示了 Arkitect 工具系统的核心能力：智能工具选择和自动执行。\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "id": "e04ee840",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "🔄 演示流式响应中的工具调用\n",
      "==================================================\n",
      "👤 用户: 深圳的天气如何？\n",
      "\n",
      "🤖 Agent 响应过程:\n",
      "   [分析用户请求...]\n",
      "   [选择工具: get_weather]\n",
      "   [调用工具: get_weather({\"city\":\"深圳\"})]\n",
      "   [工具返回: None]\n",
      "   [生成回复...]\n",
      "   [选择工具: get_weather]\n",
      "   [调用工具: get_weather({\"city\":\"深圳\"})]\n",
      "   [工具返回: meta=None content=[TextContent(type='text', text=\"([TextContent(type='text', text='深圳今天的天气：晴天，气温 32°C，微风', annotations=None, meta=None)], {'result': '深圳今天的天气：晴天，气温 32°C，微风'})\", annotations=None, meta=None)] structuredContent=None isError=False]\n",
      "   [生成回复...]\n",
      "深圳今天的天气：晴天，气温 32°C，微风\n",
      "\n",
      "==================================================\n",
      "📊 工具调用分析:\n",
      "✅ 检测到 2 次工具调用\n",
      "   🔧 工具名称: get_weather\n",
      "   📝 调用参数: {\"city\":\"深圳\"}\n",
      "   ✨ 返回结果: None\n",
      "   🔧 工具名称: get_weather\n",
      "   📝 调用参数: {\"city\":\"深圳\"}\n",
      "   ✨ 返回结果: meta=None content=[TextContent(type='text', text=\"([TextContent(type='text', text='深圳今天的天气：晴天，气温 32°C，微风', annotations=None, meta=None)], {'result': '深圳今天的天气：晴天，气温 32°C，微风'})\", annotations=None, meta=None)] structuredContent=None isError=False\n",
      "\n",
      "💡 可以看到，Agent 成功识别了用户的天气查询需求，自动选择并调用了合适的工具！\n",
      "🔍 这展示了 Arkitect 工具系统的核心能力：智能工具选择和自动执行。\n"
     ]
    }
   ],
   "source": [
    "await demonstrate_tool_chunks()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "27168fd4",
   "metadata": {
    "vscode": {
     "languageId": "raw"
    }
   },
   "source": [
    "### ⚠️ ContextInterruption：异常处理机制\n",
    "\n",
    "当工具调用出现问题时，Arkitect 使用 `ContextInterruption` 类来处理异常情况。\n",
    "\n",
    "**核心字段解析：**\n",
    "\n",
    "```python\n",
    "class ContextInterruption(BaseModel):\n",
    "    type: str                                  # 中断类型\n",
    "    message: str                               # 中断消息\n",
    "    details: Optional[dict] = None             # 详细信息\n",
    "```\n",
    "\n",
    "**关键理解：**\n",
    "- `type` 标识中断的类型（如工具调用失败、参数错误等）\n",
    "- `message` 提供人类可读的错误描述\n",
    "- `details` 包含详细的错误信息和上下文\n",
    "- ContextInterruption 确保 Agent 能够优雅地处理错误\n",
    "\n",
    "**学习 ContextInterruption 的原因：**\n",
    "- 理解 Agent 的错误处理机制\n",
    "- 掌握如何调试工具调用问题\n",
    "- 为构建健壮的生产级 Agent 奠定基础\n",
    "\n",
    "**与其他概念的联系：**\n",
    "- 当 ChatCompletionTool 执行失败时会触发 ContextInterruption\n",
    "- 中断信息会被记录在 Context 的状态中\n",
    "- 可以通过 Hooks 系统捕获和处理中断\n",
    "\n",
    "**注意：**\n",
    "- ContextInterruption是“中断信号对象”，不是异常类。\n",
    "- 不能raise/except，只能检测和消费。\n",
    "- 用法：在流式输出循环里判断chunk类型，遇到中断时做处理。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "07f6f18d",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "⚠️ 模拟工具调用异常处理\n",
      "==================================================\n",
      "\n",
      "✅ 测试正常调用:\n",
      "👤 用户: 查询北京的天气\n",
      "🤖 Agent: 北京的天气是晴天\n",
      "==============================\n",
      "\n",
      "❌ 测试异常调用:\n",
      "👤 用户: 查询error的天气\n",
      "🤖 Agent: ❌ 捕获到异常: 'NoneType' object has no attribute 'content'\n",
      "   异常类型: <class 'AttributeError'>\n",
      "\n",
      "💡 可以看到，异常被框架内部捕捉、处理并传递给Agent；而不会溢出到外部影响主程序。\n",
      "   Arkitect 框架会优雅地处理工具调用中的异常！\n"
     ]
    }
   ],
   "source": [
    "async def simulate_tool_exception():\n",
    "    \"\"\"模拟工具调用异常处理\"\"\"\n",
    "\n",
    "    print(\"⚠️ 模拟工具调用异常处理\")\n",
    "    print(\"=\" * 50)\n",
    "\n",
    "    # 创建一个会抛出异常的工具函数\n",
    "    def broken_weather(city: str) -> str:\n",
    "        \"\"\"一个会抛出异常的天气查询函数\"\"\"\n",
    "        if city == \"error\":\n",
    "            raise ValueError(\"模拟的工具调用错误\")\n",
    "        return f\"{city}的天气：晴天\"\n",
    "\n",
    "    context = Context(model=\"doubao-seed-1-6-flash-250615\", tools=[broken_weather])\n",
    "    await context.init()\n",
    "\n",
    "    # 测试正常调用\n",
    "    print(\"\\n✅ 测试正常调用:\")\n",
    "    try:\n",
    "        completion = await context.completions.create(\n",
    "            [{\"role\": \"user\", \"content\": \"查询北京的天气\"}], stream=True\n",
    "        )\n",
    "\n",
    "        print(\"👤 用户: 查询北京的天气\")\n",
    "        print(\"🤖 Agent: \", end=\"\")\n",
    "\n",
    "        async for chunk in completion:\n",
    "            if hasattr(chunk, \"choices\") and chunk.choices:\n",
    "                delta = chunk.choices[0].delta\n",
    "                if hasattr(delta, \"content\") and delta.content:\n",
    "                    print(delta.content, end=\"\")\n",
    "\n",
    "    except Exception as e:\n",
    "        print(f\"❌ 捕获到异常: {e}\")\n",
    "        print(f\"   异常类型: {type(e)}\")\n",
    "\n",
    "    print(\"\\n\" + \"=\" * 30)\n",
    "\n",
    "    # 测试异常调用\n",
    "    print(\"\\n❌ 测试异常调用:\")\n",
    "    try:\n",
    "        completion = await context.completions.create(\n",
    "            [{\"role\": \"user\", \"content\": \"查询error的天气\"}], stream=True\n",
    "        )\n",
    "\n",
    "        print(\"👤 用户: 查询error的天气\")\n",
    "        print(\"🤖 Agent: \", end=\"\")\n",
    "\n",
    "        async for chunk in completion:\n",
    "            if hasattr(chunk, \"choices\") and chunk.choices:\n",
    "                delta = chunk.choices[0].delta\n",
    "                if hasattr(delta, \"content\") and delta.content:\n",
    "                    print(delta.content, end=\"\")\n",
    "\n",
    "        print()\n",
    "\n",
    "    except Exception as e:\n",
    "        print(f\"❌ 捕获到异常: {e}\")\n",
    "        print(f\"   异常类型: {type(e)}\")\n",
    "\n",
    "    print(\n",
    "        \"\\n💡 可以看到，异常被框架内部捕捉、处理并传递给Agent；\"\n",
    "        \"而不会溢出到外部影响主程序。\\n\"\n",
    "        \"Arkitect 框架会优雅地处理工具调用中的异常！\"\n",
    "    )\n",
    "\n",
    "\n",
    "# 运行异常处理演示\n",
    "await simulate_tool_exception()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "43eeede2",
   "metadata": {
    "vscode": {
     "languageId": "raw"
    }
   },
   "source": [
    "### 🎯 第 2 章总结\n",
    "\n",
    "通过本章的学习，您已经掌握了如何构建一个功能完整的 Agent：\n",
    "\n",
    "#### 1. **工具系统核心概念**\n",
    "- ✅ 理解了 Agent 与普通 Context 的区别\n",
    "- ✅ 掌握了如何将 Python 函数转换为 AI 可调用的工具\n",
    "- ✅ 学会了工具注册和参数配置的方法\n",
    "\n",
    "#### 2. **ChatCompletionTool（工具转换）**\n",
    "- ✅ 理解了 ChatCompletionTool 的内部结构\n",
    "- ✅ 掌握了 `from_function()` 方法的使用\n",
    "- ✅ 学会了如何检查工具的 JSON Schema\n",
    "\n",
    "#### 3. **ToolChunk（流式工具调用）**\n",
    "- ✅ 理解了流式响应中工具调用的处理机制\n",
    "- ✅ 掌握了如何在流式输出中识别工具调用\n",
    "- ✅ 学会了工具调用的数据流转过程\n",
    "\n",
    "#### 4. **ContextInterruption（异常处理）**\n",
    "- ✅ 理解了 Agent 的错误处理机制\n",
    "- ✅ 掌握了如何模拟和处理工具调用异常\n",
    "- ✅ 学会了构建健壮 Agent 的基本原则\n",
    "\n",
    "#### 🔑 **关键要点回顾**\n",
    "1. **Agent = Context + Tools**：Agent 是配备了工具的智能 Context\n",
    "2. **ChatCompletionTool = 函数桥梁**：连接 Python 函数和 LLM 的桥梁\n",
    "3. **ToolChunk = 流式处理**：处理流式响应中的工具调用\n",
    "4. **ContextInterruption = 异常处理**：确保 Agent 的健壮性\n",
    "\n",
    "#### 🔄 **数据流总结**\n",
    "```\n",
    "用户请求 → Context → LLM 分析 → 选择工具 → 执行 Python 函数 → 返回结果 → 整合回复\n",
    "```\n",
    "\n",
    "#### 🚀 **下一步**\n",
    "在下一章中，我们将学习如何通过 `ArkChatParameters` 精确控制 Agent 的行为！\n",
    "\n",
    "---\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3965e828",
   "metadata": {},
   "source": [
    "## 第 3 章：精确控制 Agent 行为 - ArkChatParameters\n",
    "\n",
    "在前两章中，我们学会了创建 Context 和构建 Agent。但是，要让 Agent 在不同场景下表现出最佳性能，我们需要精确控制它的行为。这就是 `ArkChatParameters` 的作用。\n",
    "\n",
    "### 为什么需要参数控制？\n",
    "\n",
    "不同的任务需要不同的 AI 行为模式：\n",
    "\n",
    "- 🎨 **创意写作**：需要高创造性，允许更多随机性\n",
    "- 📊 **数据分析**：需要精确性，减少随机性\n",
    "- 🔧 **工具调用**：需要准确性，避免幻觉\n",
    "- 💬 **日常对话**：需要平衡，既自然又准确\n",
    "\n",
    "`ArkChatParameters` 让您能够为每种场景量身定制 Agent 的行为特征。\n",
    "\n",
    "### 参数控制的核心价值\n",
    "\n",
    "通过参数控制，您可以：\n",
    "\n",
    "1. **优化任务性能**：针对特定任务调整 AI 行为\n",
    "2. **控制成本**：通过 `max_tokens` 限制输出长度\n",
    "3. **提升用户体验**：通过 `temperature` 调节回复风格\n",
    "4. **确保一致性**：通过参数组合获得稳定的输出质量\n",
    "\n",
    "让我们通过实际实验来深入理解这些概念！\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dd566e43",
   "metadata": {
    "vscode": {
     "languageId": "raw"
    }
   },
   "source": [
    "### 🔧 ArkChatParameters：参数控制的核心\n",
    "\n",
    "`ArkChatParameters` 是 Arkitect 框架中用于精确控制 AI 行为的核心类。它允许您微调模型的响应方式，优化不同场景下的性能表现。\n",
    "\n",
    "**核心字段解析：**\n",
    "\n",
    "```python\n",
    "class ArkChatParameters(BaseModel):\n",
    "    # 基础控制参数\n",
    "    temperature: Optional[float] = None          # 控制输出的随机性和创造性\n",
    "    max_tokens: Optional[int] = None             # 限制生成的最大token数量\n",
    "    top_p: Optional[float] = None                # 核采样参数，控制词汇选择范围\n",
    "    \n",
    "    # 重复性控制参数\n",
    "    frequency_penalty: Optional[float] = None    # 频率惩罚，减少重复内容\n",
    "    presence_penalty: Optional[float] = None     # 存在惩罚，鼓励话题多样性\n",
    "    \n",
    "    # 输出控制参数\n",
    "    stop: Optional[Union[str, List[str]]] = None # 停止词，控制生成结束条件\n",
    "    n: Optional[int] = Field(default=1, ge=1, le=5)  # 生成选择的数量\n",
    "    \n",
    "    # 高级功能参数\n",
    "    tools: Optional[List[ChatCompletionTool]] = None  # 工具列表\n",
    "    thinking: Optional[Thinking] = None          # 思考模式配置（豆包特有）\n",
    "    logprobs: Optional[bool] = None              # 是否返回对数概率\n",
    "    top_logprobs: Optional[int] = None           # 返回的top概率数量\n",
    "    \n",
    "    # 其他参数\n",
    "    logit_bias: Optional[Dict[str, int]] = None  # 词汇偏置调整\n",
    "    stream_options: Optional[ChatCompletionStreamOptionsParam] = None  # 流式选项\n",
    "```\n",
    "\n",
    "**关键参数详解：**\n",
    "\n",
    "#### 🌡️ **temperature (温度)**\n",
    "- **范围**: 0.0 - 2.0\n",
    "- **作用**: 控制输出的随机性和创造性\n",
    "- **低值 (0.0-0.3)**: 输出更确定、一致，适合事实性任务\n",
    "- **中值 (0.4-0.8)**: 平衡创造性和准确性，适合大多数对话\n",
    "- **高值 (0.9-2.0)**: 输出更有创造性和随机性，适合创意写作\n",
    "\n",
    "#### 🎯 **max_tokens (最大token数)**\n",
    "- **作用**: 限制生成回复的最大长度\n",
    "- **用途**: 控制成本、确保回复简洁、避免过长输出\n",
    "- **建议**: 根据具体需求设置，一般对话可设置为 100-1000\n",
    "\n",
    "#### 🔄 **top_p (核采样)**\n",
    "- **范围**: 0.0 - 1.0\n",
    "- **作用**: 控制词汇选择的范围，只考虑累积概率达到 top_p 的词汇\n",
    "- **低值**: 更保守的词汇选择\n",
    "- **高值**: 更多样化的词汇选择\n",
    "\n",
    "#### 🚫 **frequency_penalty (频率惩罚)**\n",
    "- **范围**: -2.0 - 2.0\n",
    "- **作用**: 根据词汇在文本中的出现频率进行惩罚\n",
    "- **正值**: 减少重复内容\n",
    "- **负值**: 增加重复可能性\n",
    "\n",
    "#### 🎪 **presence_penalty (存在惩罚)**\n",
    "- **范围**: -2.0 - 2.0\n",
    "- **作用**: 根据词汇是否已经出现过进行惩罚\n",
    "- **正值**: 鼓励引入新话题和词汇\n",
    "- **负值**: 倾向于使用已出现的词汇\n",
    "\n",
    "#### ⏹️ **stop (停止词)**\n",
    "- **作用**: 指定生成停止的标志词或短语\n",
    "- **类型**: 可以是单个字符串或字符串列表\n",
    "- **用途**: 控制输出格式、防止生成不当内容\n",
    "\n",
    "#### 🔢 **n (生成数量)**\n",
    "- **范围**: 1 - 5\n",
    "- **默认值**: 1\n",
    "- **作用**: 指定为每个输入生成多少个不同的回复选择\n",
    "- **注意**: 生成多个选择会增加token消耗和成本\n",
    "- **建议**: 除非特殊需求，否则保持默认值 1\n",
    "\n",
    "#### 🛠️ **高级功能参数**\n",
    "\n",
    "##### **🧠 thinking (思考模式) - 「豆包特有功能」**\n",
    "- **类型**: `Thinking`\n",
    "- **设置**: `Thinking(type=\"enabled/disabled/auto\")`\n",
    "- **作用**: 豆包模型的“思考和推理能力”\n",
    "- **特点**: 增强 Agent 的推理能力\n",
    "- **用途**: 复杂推理任务\n",
    "\n",
    "##### **logprobs (对数概率)**\n",
    "- **类型**: `bool`\n",
    "- **作用**: 是否返回生成token的对数概率\n",
    "- **用途**: 分析模型的确信度、调试、研究\n",
    "\n",
    "##### **top_logprobs (top概率数量)**\n",
    "- **类型**: `int`\n",
    "- **作用**: 返回每个位置的top-k个候选token及其概率\n",
    "- **前提**: 需要 `logprobs=True`\n",
    "- **用途**: 深度分析模型的选择过程\n",
    "\n",
    "#### 🔧 **其他参数**\n",
    "\n",
    "##### **logit_bias (词汇偏置)**\n",
    "- **类型**: `Dict[str, int]`\n",
    "- **范围**: 值在 -100 到 100 之间\n",
    "- **作用**: 调整特定token的出现概率\n",
    "- **用途**: 增加或减少特定词汇的使用频率\n",
    "- **示例**: `{\"Python\": 10, \"Java\": -10}` 会增加\"Python\"出现概率，减少\"Java\"\n",
    "\n",
    "##### **stream_options (流式选项)**\n",
    "- **类型**: `ChatCompletionStreamOptionsParam`\n",
    "- **作用**: 配置流式响应的行为\n",
    "- **用途**: 控制流式输出的格式和包含的信息\n",
    "\n",
    "**学习 ArkChatParameters 的原因：**\n",
    "- 它是精确控制 Agent 行为的核心工具\n",
    "- 不同任务需要不同的参数配置优化\n",
    "- 理解参数有助于优化成本和性能\n",
    "- 为高级 Agent 定制奠定基础\n",
    "\n",
    "**与其他概念的联系：**\n",
    "- State 的 `parameters` 字段存储 ArkChatParameters 实例\n",
    "- ArkChatRequest 继承了这些参数用于实际请求\n",
    "- 工具调用通过 `tools` 参数进行配置\n",
    "- 参数会影响 ArkChatResponse 的生成结果"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "id": "dd26048b",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "🌡️ Temperature 参数实验\n",
      "==================================================\n",
      "\n",
      "🌡️ Temperature = 0.1\n",
      "------------------------------\n",
      "👤 用户: 用一句话描述春天的美好\n",
      "🤖 AI (temp=0.1): 春天是那漫山遍野绽放的绚烂花朵与轻拂脸庞的温柔微风交织而成的梦幻诗篇，处处弥漫着生机与浪漫的美好气息。\n",
      "\n",
      "🌡️ Temperature = 0.7\n",
      "------------------------------\n",
      "👤 用户: 用一句话描述春天的美好\n",
      "🤖 AI (temp=0.7): 春天是那漫山遍野绽放的绚烂花朵与轻柔春风交织出的如梦似幻的诗意画卷，处处弥漫着生机与温柔的美好。\n",
      "\n",
      "🌡️ Temperature = 1.5\n",
      "------------------------------\n",
      "👤 用户: 用一句话描述春天的美好\n",
      "🤖 AI (temp=1.5): 春日里繁花似锦，绿柳垂丝，处处弥漫着生机与温柔的美好景致。\n",
      "\n",
      "==================================================\n",
      "📊 实验结果分析:\n",
      "   🔹 Temperature = 0.1: 输出更加一致和保守\n",
      "   🔹 Temperature = 0.7: 平衡创造性和一致性\n",
      "   🔹 Temperature = 1.5: 更具创造性，但可能不够稳定\n"
     ]
    }
   ],
   "source": [
    "async def temperature_experiment():\n",
    "    \"\"\"实验：Temperature 参数对创造性的影响\"\"\"\n",
    "\n",
    "    print(\"🌡️ Temperature 参数实验\")\n",
    "    print(\"=\" * 50)\n",
    "\n",
    "    # 测试提示：要求创造性回答\n",
    "    prompt = \"用一句话描述春天的美好\"\n",
    "\n",
    "    # 测试不同的 temperature 值\n",
    "    temperatures = [0.1, 0.7, 1.5]\n",
    "\n",
    "    MODEL = \"doubao-seed-1-6-flash-250615\"\n",
    "\n",
    "    for temp in temperatures:\n",
    "        print(f\"\\n🌡️ Temperature = {temp}\")\n",
    "        print(\"-\" * 30)\n",
    "\n",
    "        # 创建带有特定 temperature 的参数\n",
    "        params = ArkChatParameters(temperature=temp, max_tokens=50)\n",
    "\n",
    "        # 创建新的 Context\n",
    "        context = Context(model=MODEL, parameters=params)\n",
    "        await context.init()\n",
    "\n",
    "        # 发送请求\n",
    "        completion = await context.completions.create(\n",
    "            [{\"role\": \"user\", \"content\": prompt}], stream=True\n",
    "        )\n",
    "\n",
    "        print(f\"👤 用户: {prompt}\")\n",
    "        print(f\"🤖 AI (temp={temp}): \", end=\"\")\n",
    "\n",
    "        async for chunk in completion:\n",
    "            if hasattr(chunk, \"choices\") and chunk.choices:\n",
    "                delta = chunk.choices[0].delta\n",
    "                if hasattr(delta, \"content\") and delta.content:\n",
    "                    print(delta.content, end=\"\")\n",
    "\n",
    "        print()\n",
    "\n",
    "    print(\"\\n\" + \"=\" * 50)\n",
    "    print(\"📊 实验结果分析:\")\n",
    "    print(\"   🔹 Temperature = 0.1: 输出更加一致和保守\")\n",
    "    print(\"   🔹 Temperature = 0.7: 平衡创造性和一致性\")\n",
    "    print(\"   🔹 Temperature = 1.5: 更具创造性，但可能不够稳定\")\n",
    "\n",
    "\n",
    "# 运行实验\n",
    "await temperature_experiment()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "id": "f6b22eb3",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "🎛️ 综合参数控制实验\n",
      "==================================================\n",
      "\n",
      "🔧 工具调用场景\n",
      "----------------------------------------\n",
      "📝 参数配置: 低温度确保工具调用准确性\n",
      "⚙️ 参数详情: temp=0.1, max_tokens=100\n",
      "👤 用户: 请查询北京的天气\n",
      "🤖 AI: 北京今天的天气：晴天，气温 25°C，微风\n",
      "📏 实际输出长度: 21 字符\n",
      "\n",
      "\n",
      "🎨 创意写作场景\n",
      "----------------------------------------\n",
      "📝 参数配置: 高温度激发创造力，frequency_penalty减少重复\n",
      "⚙️ 参数详情: temp=0.8, max_tokens=150\n",
      "👤 用户: 写一首关于春天的小诗\n",
      "🤖 AI: 《春信》\n",
      "\n",
      "微风轻拨柳丝弦，\n",
      "桃蕊偷将粉面嫣。\n",
      "草色初描天际线，\n",
      "燕声唤醒梦中田。\n",
      "\n",
      "溪融薄冰流韵致，\n",
      "日暖柔云作画笺。\n",
      "最是一年春好处，\n",
      "芳菲满径送诗缘。\n",
      "📏 实际输出长度: 78 字符\n",
      "\n",
      "\n",
      "📊 精确回答场景\n",
      "----------------------------------------\n",
      "📝 参数配置: 低温度保证准确性，stop词控制长度\n",
      "⚙️ 参数详情: temp=0.2, max_tokens=80\n",
      "👤 用户: 什么是人工智能？请简洁回答\n",
      "🤖 AI: 人工智能（Artificial Intelligence，简称AI）是让机器模拟、延伸和扩展人类智能，使其能像人类一样感知、学习、推理、决策并执行任务的理论、方法、技术及应用系统。\n",
      "📏 实际输出长度: 90 字符\n",
      "\n",
      "==================================================\n"
     ]
    }
   ],
   "source": [
    "async def parameters_comprehensive_experiment():\n",
    "    \"\"\"实验：综合参数控制演示\"\"\"\n",
    "\n",
    "    print(\"🎛️ 综合参数控制实验\")\n",
    "    print(\"=\" * 50)\n",
    "\n",
    "    # 测试场景：不同任务需要不同的参数配置\n",
    "    scenarios = [\n",
    "        {\n",
    "            \"name\": \"🔧 工具调用场景\",\n",
    "            \"prompt\": \"请查询北京的天气\",\n",
    "            \"params\": ArkChatParameters(temperature=0.1, max_tokens=100),\n",
    "            \"description\": \"低温度确保工具调用准确性\",\n",
    "        },\n",
    "        {\n",
    "            \"name\": \"🎨 创意写作场景\",\n",
    "            \"prompt\": \"写一首关于春天的小诗\",\n",
    "            \"params\": ArkChatParameters(\n",
    "                temperature=0.8, max_tokens=150, frequency_penalty=0.5\n",
    "            ),\n",
    "            \"description\": \"高温度激发创造力，frequency_penalty减少重复\",\n",
    "        },\n",
    "        {\n",
    "            \"name\": \"📊 精确回答场景\",\n",
    "            \"prompt\": \"什么是人工智能？请简洁回答\",\n",
    "            \"params\": ArkChatParameters(\n",
    "                temperature=0.2, max_tokens=80, stop=[\"。\", \"！\"]\n",
    "            ),\n",
    "            \"description\": \"低温度保证准确性，stop词控制长度\",\n",
    "        },\n",
    "    ]\n",
    "\n",
    "    MODEL = \"doubao-seed-1-6-flash-250615\"\n",
    "\n",
    "    for scenario in scenarios:\n",
    "        print(f\"\\n{scenario['name']}\")\n",
    "        print(\"-\" * 40)\n",
    "        print(f\"📝 参数配置: {scenario['description']}\")\n",
    "        print(\n",
    "            f\"⚙️ 参数详情: temp={scenario['params'].temperature}, \"\n",
    "            f\"max_tokens={scenario['params'].max_tokens}\"\n",
    "        )\n",
    "\n",
    "        # 创建Context\n",
    "        context = Context(\n",
    "            model=MODEL,\n",
    "            parameters=scenario[\"params\"],\n",
    "            tools=[get_weather] if \"工具\" in scenario[\"name\"] else None,\n",
    "        )\n",
    "        await context.init()\n",
    "\n",
    "        # 发送请求\n",
    "        completion = await context.completions.create(\n",
    "            [{\"role\": \"user\", \"content\": scenario[\"prompt\"]}], stream=True\n",
    "        )\n",
    "\n",
    "        print(f\"👤 用户: {scenario['prompt']}\")\n",
    "        print(\"🤖 AI: \", end=\"\")\n",
    "\n",
    "        response_text = \"\"\n",
    "        async for chunk in completion:\n",
    "            if hasattr(chunk, \"choices\") and chunk.choices:\n",
    "                delta = chunk.choices[0].delta\n",
    "                if hasattr(delta, \"content\") and delta.content:\n",
    "                    response_text += delta.content\n",
    "                    print(delta.content, end=\"\")\n",
    "\n",
    "        print(f\"\\n📏 实际输出长度: {len(response_text)} 字符\")\n",
    "        print()\n",
    "\n",
    "    print(\"=\" * 50)\n",
    "\n",
    "\n",
    "# 运行综合实验\n",
    "await parameters_comprehensive_experiment()"
   ]
  },
  {
   "cell_type": "raw",
   "id": "6a03b4be",
   "metadata": {
    "vscode": {
     "languageId": "raw"
    }
   },
   "source": [
    "### 🧠 特殊参数演示：thinking - 解放豆包大模型的推理能力\n",
    "\n",
    "#### 概述\n",
    "\n",
    "上面的例子展示了一般的 `ArkChatParameters` 如何影响 Agent 的回复。下面我们将介绍一个特殊的参数来解放「豆包」大模型的推理能力。\n",
    "\n",
    "#### 重要说明\n",
    "\n",
    "**模型支持情况：**\n",
    "- ✅ `doubao-seed-1-6-250615`：支持，需要手动设置 `thinking` 参数\n",
    "- ✅ `doubao-seed-1-6-flash-250615`：支持，需要手动设置 `thinking` 参数  \n",
    "- ⚠️ `doubao-seed-1-6-thinking-250615`：默认开启 thinking，无需设置此参数\n",
    "\n",
    "**thinking 参数的真正作用：**\n",
    "- `thinking` 参数不仅仅是显示思考过程，更重要的是**提升了模型的推理能力**\n",
    "- 开启 thinking 后，模型会进行更深层的思考，从而给出更准确、更有逻辑的答案\n",
    "- 这对于需要复杂推理的任务特别有效\n",
    "\n",
    "#### 演示：thinking 参数对推理能力的提升\n",
    "\n",
    "我们使用一个复杂的论述题目来展示 thinking 参数的效果，采用 tool_ability_tester 中探索过的成熟测试方法。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2bd9e6ab",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "🧠 thinking 参数推理能力演示\n",
      "==================================================\n",
      "🔍 场景 1：普通模式（不开启 thinking）\n",
      "----------------------------------------\n",
      "👤 用户: 逻辑推理题目\n",
      "🤖 普通模式回答:\n",
      "这是一道关于判断命题之间是否矛盾以及分析其同时为真条件的逻辑问题。破题点在于明确两个命题的逻辑关系，并通过对概念范围的分析来判断。\n",
      "\n",
      "### 步骤一：分析两个前提的逻辑形式\n",
      "- “所有聪明的人都会学习逻辑”可以表示为“聪明的人→学习逻辑”。\n",
      "- “有些逻辑学习者并不聪明”可以表示为“有的学习逻辑的人→¬聪明”。\n",
      "\n",
      "### 步骤二：判断是否矛盾\n",
      "- 这两个前提并不矛盾。因为“所有聪明的人都会学习逻辑”只是说聪明的人包含在学习逻辑的人这个范畴里，但学习逻辑的人不一定只有聪明的人，还可能有不聪明的人，所以“有些逻辑学习者并不聪明”是有可能存在的，两者可以同时成立。\n",
      "\n",
      "### 步骤三：分析同时为真的条件\n",
      "- 当“学习逻辑的人”这个集合除了包含所有聪明的人之外，还存在一部分不聪明的人时，这两个前提就可以同时为真。也就是说，只要聪明的人是学习逻辑的人的子集，并且学习逻辑的人还有非聪明的元素，那么这两个前提就能同时为真。例如，聪明的人都在学习逻辑的群体里，同时还有一些不聪明的人也在学习逻辑，此时“所有聪明的人都会学习逻辑”和“有些逻辑学习者并不聪明”就可以同时为真。\n",
      "\n",
      "综上，上述两个前提不矛盾，当学习逻辑的人群中除了所有聪明的人之外还有不聪明的人时，它们可以同时为真。\n",
      "\n",
      "🧠 推理过程: 无推理\n",
      "\n",
      "==================================================\n",
      "🔍 场景 2：开启 thinking 模式\n",
      "----------------------------------------\n",
      "👤 用户: 逻辑推理题目\n",
      "🤖 thinking 模式回答:\n",
      "### 步骤1：分析两个前提的逻辑形式  \n",
      "- 第一个前提“所有聪明的人都会学习逻辑”是全称肯定命题，逻辑形式为：**所有S都是P**（设S为“聪明的人”，P为“学习逻辑的人”），即 \\( \\forall x (S(x) \\to P(x)) \\)。  \n",
      "- 第二个前提“有些逻辑学习者并不聪明”是特称否定命题，逻辑形式为：**有的P不是S**，即 \\( \\exists x (P(x) \\land \\neg S(x)) \\)。  \n",
      "\n",
      "\n",
      "### 步骤2：判断是否矛盾  \n",
      "全称肯定命题 \\( \\forall x (S(x) \\to P(x)) \\) 与特称否定命题 \\( \\exists x (P(x) \\land \\neg S(x)) \\) 并不矛盾。  \n",
      "因为“所有S都是P”只要求聪明的人都属于学习逻辑的人，但允许存在部分学习逻辑的人不属于聪明的人。例如：  \n",
      "- 集合S（聪明的人）是集合P（学习逻辑的人）的子集，即 \\( S \\subseteq P \\)，但P中存在不属于S的元素。此时，“所有聪明的人都会学习逻辑”（\\( \\forall x (S(x) \\to P(x)) \\)）为真，“有些逻辑学习者并不聪明”（\\( \\exists x (P(x) \\land \\neg S(x)) \\)）也为真。  \n",
      "\n",
      "\n",
      "### 步骤3：分析同时为真的条件  \n",
      "当且仅当**存在学习逻辑的人不属于聪明的人集合，且所有聪明的人都属于学习逻辑的人集合**时，两个前提可以同时为真。  \n",
      "具体来说，设集合 \\( P \\)（学习逻辑的人）包含两部分：一部分是集合 \\( S \\)（聪明的人），另一部分是不属于 \\( S \\) 的元素。即 \\( P = S \\cup Q \\)，其中 \\( Q \\neq \\varnothing \\) 且 \\( Q \\cap S = \\varnothing \\)。此时：  \n",
      "- “所有聪明的人都会学习逻辑”（\\( \\forall x (S(x) \\to P(x)) \\)）为真；  \n",
      "- “有些逻辑学习者并不聪明”（\\( \\exists x (P(x) \\land \\neg S(x)) \\)）也为真。  \n",
      "\n",
      "\n",
      "综上，两个前提**不矛盾**，当且仅当存在学习逻辑的人不属于聪明的人集合且所有聪明的人都属于学习逻辑的人集合时，两者可以同时为真。\n",
      "\n",
      "🧠 推理过程:\n",
      "用户现在需要分析这两个前提是否矛盾以及在什么条件下可以同时为真。首先，得明确第一个前提“所有聪明的人都会学习逻辑”是全称肯定命题，逻辑形式是“所有S都是P”，这里S是“聪明的人”，P是“学习逻辑的人”。第二个前提“有些逻辑学习者并不聪明”是特称否定命题，逻辑形式是“有的P不是S”。接下来，要考虑这两个命题之间的逻辑关系。首先，“所有S都是P”的换位是“有的P是S”，但第二个前提是“有的P不是S”，这两个命题是下反对关系吗？不对，其实全称肯定和特称否定是矛盾关系吗？不，全称肯定“所有S都是P”和特称否定“有的P不是S”其实是可以同时存在的。比如，假设存在一些人是聪明的，他们都学习逻辑，而另外一些学习逻辑的人不聪明，这时候两个前提就可以同时为真。所以首先要明确它们不矛盾，然后分析在什么情况下同时为真。首先，第一个前提“所有聪明的人都会学习逻辑”可以表示为S→P（如果是聪明的人，那么学习逻辑）。第二个前提“有些逻辑学习者不聪明”是有的P∧¬S。要让这两个同时为真，需要存在至少一个P，使得P∧¬S为真，同时所有S都满足S→P。比如，设集合S是聪明的人，P是学习逻辑的人，存在一些元素在P里但不在S里，同时所有S里的元素都在P里，这样两个前提就可以同时为真。所以总结：两个前提不矛盾，当存在学习逻辑的人不属于聪明的人集合，同时所有聪明的人都属于学习逻辑的人集合时，两者可以同时为真。\n",
      "==================================================\n",
      "\n",
      "📊 分析输出差异\n",
      "----------------------------------------\n",
      "🔍 普通模式 Usage:\n",
      "   输入 tokens: 53\n",
      "   输出 tokens: 345\n",
      "   总计 tokens: 398\n",
      "   推理 tokens: 0\n",
      "\n",
      "🔍 thinking 模式 Usage:\n",
      "   输入 tokens: 101\n",
      "   输出 tokens: 968\n",
      "   总计 tokens: 1069\n",
      "   推理 tokens: 405\n",
      "\n",
      "比较结果：可以看出，thinking模式提供了更为深入的推理过程，但代价是token使用量增加，其推理时间也会相应增加\n"
     ]
    }
   ],
   "source": [
    "from volcenginesdkarkruntime.types.chat.completion_create_params import Thinking\n",
    "\n",
    "\n",
    "async def demonstrate_thinking():\n",
    "    \"\"\"演示 thinking 参数对推理能力的提升\"\"\"\n",
    "\n",
    "    print(\"🧠 thinking 参数推理能力演示\")\n",
    "    print(\"=\" * 50)\n",
    "\n",
    "    #\n",
    "    reasoning_question = \"\"\"\n",
    "    所有聪明的人都会学习逻辑。\n",
    "    有些逻辑学习者并不聪明。\n",
    "    上述两个前提是否矛盾？请说明理由，并分析在什么条件下它们可以同时为真\n",
    "    \"\"\"\n",
    "\n",
    "    MODEL = \"doubao-seed-1-6-flash-250615\"\n",
    "\n",
    "    # 场景1：普通模式（不开启 thinking）\n",
    "    print(\"🔍 场景 1：普通模式（不开启 thinking）\")\n",
    "    print(\"-\" * 40)\n",
    "\n",
    "    normal_context = Context(model=MODEL)\n",
    "    await normal_context.init()\n",
    "\n",
    "    completion = await normal_context.completions.create(\n",
    "        [{\"role\": \"user\", \"content\": reasoning_question}],\n",
    "        thinking=Thinking(type=\"disabled\"),\n",
    "        stream=False,\n",
    "    )\n",
    "\n",
    "    print(\"👤 用户: 逻辑推理题目\")\n",
    "    print(\"🤖 普通模式回答:\")\n",
    "\n",
    "    normal_reasoning_content = \"\"\n",
    "\n",
    "    if (\n",
    "        hasattr(completion.choices[0].message, \"content\")\n",
    "        and completion.choices[0].message.content\n",
    "    ):\n",
    "        print(completion.choices[0].message.content)\n",
    "    else:\n",
    "        print(\"🤖 普通模式回答: 无回答\")\n",
    "\n",
    "    if (\n",
    "        hasattr(completion.choices[0].message, \"reasoning_content\")\n",
    "        and completion.choices[0].message.reasoning_content\n",
    "    ):\n",
    "        normal_reasoning_content = completion.choices[0].message.reasoning_content\n",
    "    if hasattr(completion, \"usage\") and completion.usage:\n",
    "        normal_usage = completion.usage\n",
    "\n",
    "    if normal_reasoning_content != \"\":\n",
    "        print(\"\\n🧠 推理过程:\")\n",
    "        print(normal_reasoning_content)\n",
    "    else:\n",
    "        print(\"\\n🧠 推理过程: 无推理\")\n",
    "\n",
    "    print(\"\\n\" + \"=\" * 50)\n",
    "\n",
    "    # 场景2：开启 thinking 模式\n",
    "    print(\"🔍 场景 2：开启 thinking 模式\")\n",
    "    print(\"-\" * 40)\n",
    "\n",
    "    thinking_context = Context(\n",
    "        model=MODEL, parameters=ArkChatParameters(thinking={\"type\": \"enabled\"})\n",
    "    )\n",
    "    await thinking_context.init()\n",
    "\n",
    "    completion = await thinking_context.completions.create(\n",
    "        [{\"role\": \"user\", \"content\": reasoning_question}], stream=False\n",
    "    )\n",
    "\n",
    "    print(\"👤 用户: 逻辑推理题目\")\n",
    "    print(\"🤖 thinking 模式回答:\")\n",
    "\n",
    "    thinking_reasoning_content = \"\"\n",
    "\n",
    "    if (\n",
    "        hasattr(completion.choices[0].message, \"content\")\n",
    "        and completion.choices[0].message.content\n",
    "    ):\n",
    "        print(completion.choices[0].message.content)\n",
    "    else:\n",
    "        print(\"🤖 thinking 模式回答: 无回答\")\n",
    "\n",
    "    if (\n",
    "        hasattr(completion.choices[0].message, \"reasoning_content\")\n",
    "        and completion.choices[0].message.reasoning_content\n",
    "    ):\n",
    "        thinking_reasoning_content = completion.choices[0].message.reasoning_content\n",
    "    if hasattr(completion, \"usage\") and completion.usage:\n",
    "        thinking_usage = completion.usage\n",
    "\n",
    "    if thinking_reasoning_content != \"\":\n",
    "        print(\"\\n🧠 推理过程:\")\n",
    "        print(thinking_reasoning_content)\n",
    "    else:\n",
    "        print(\"\\n🧠 推理过程: 无推理\")\n",
    "\n",
    "    print(\"=\" * 50)\n",
    "    print(\"\\n📊 分析输出差异\")\n",
    "    print(\"-\" * 40)\n",
    "\n",
    "    print(\"🔍 普通模式 Usage:\")\n",
    "    if normal_usage:\n",
    "        print(f\"   输入 tokens: {normal_usage.prompt_tokens}\")\n",
    "        print(f\"   输出 tokens: {normal_usage.completion_tokens}\")\n",
    "        print(f\"   总计 tokens: {normal_usage.total_tokens}\")\n",
    "        if hasattr(normal_usage, \"completion_tokens_details\"):\n",
    "            print(\n",
    "                f\"   推理 tokens: {\n",
    "                    normal_usage.completion_tokens_details.reasoning_tokens\n",
    "                }\"\n",
    "            )\n",
    "    else:\n",
    "        print(\"🔍 普通模式 Usage: 无使用统计\")\n",
    "\n",
    "    print(\"\\n🔍 thinking 模式 Usage:\")\n",
    "    if thinking_usage:\n",
    "        print(f\"   输入 tokens: {thinking_usage.prompt_tokens}\")\n",
    "        print(f\"   输出 tokens: {thinking_usage.completion_tokens}\")\n",
    "        print(f\"   总计 tokens: {thinking_usage.total_tokens}\")\n",
    "        if hasattr(thinking_usage, \"completion_tokens_details\"):\n",
    "            print(\n",
    "                f\"   推理 tokens: {\n",
    "                    thinking_usage.completion_tokens_details.reasoning_tokens\n",
    "                }\"\n",
    "            )\n",
    "    else:\n",
    "        print(\"🔍 thinking 模式 Usage: 无使用统计\")\n",
    "\n",
    "    print(\n",
    "        \"\\n比较结果：可以看出，thinking模式提供了更为深入的推理过程，但代价是token使用量增加，其推理时间也会相应增加\"\n",
    "    )\n",
    "\n",
    "    return completion\n",
    "\n",
    "\n",
    "# 运行演示\n",
    "completion = await demonstrate_thinking()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4bc6bc16",
   "metadata": {},
   "source": [
    "#### 为什么 stream=True 时无法获取 usage 信息？\n",
    "\n",
    "在 Arkitect 以及大多数大模型 API 框架中，流式（stream=True）和非流式（stream=False）响应有本质区别：\n",
    "\n",
    "- **stream=False（非流式）**  \n",
    "  - 一次性返回完整响应对象（如 ArkChatResponse）。\n",
    "  - 响应对象自带 usage 字段（如 prompt_tokens、completion_tokens、total_tokens）。\n",
    "  - 可以直接通过 `response.usage` 获取本次请求的 token 使用情况。\n",
    "\n",
    "- **stream=True（流式）**  \n",
    "  - 服务端将响应内容拆分为多个 chunk，逐步推送给客户端。\n",
    "  - 每个 chunk 只包含本次增量内容（如 delta.content），**绝大多数实现不会在 chunk 里返回 usage 字段**。\n",
    "  - usage 统计需要等模型生成全部内容后才能统计，流式 chunk 设计上就是“边生成边发”，服务端此时还不知道最终的 token 总数。\n",
    "  - 有些实现会在**最后一个 chunk**附带 usage 字段，但大多数情况下不会，且前面的 chunk 一定没有 usage。\n",
    "  - **因此，stream=True 时，无法像非流式模式那样直接获取 usage 信息。**\n",
    "\n",
    "**实践建议：**  \n",
    "- 需要准确 usage 统计时，请使用 `stream=False`，直接 await 得到完整响应对象。\n",
    "- 需要实时输出时只能用 `stream=True`，但通常无法获得 usage 信息。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4cb65dbd",
   "metadata": {
    "vscode": {
     "languageId": "raw"
    }
   },
   "source": [
    "### 📨 ArkChatRequest：请求的完整生命周期\n",
    "\n",
    "现在让我们深入了解 `ArkChatRequest` 类，它定义了与 LLM 交互的请求格式。\n",
    "\n",
    "**核心字段解析：**\n",
    "\n",
    "```python\n",
    "class ArkChatRequest(Request):\n",
    "    messages: List[ArkMessage]                    # 对话消息列表\n",
    "    model: str                                    # 模型名称\n",
    "    \n",
    "    # 行为控制参数\n",
    "    temperature: Optional[float] = None           # 创造性控制\n",
    "    max_tokens: Optional[int] = None             # 输出长度限制\n",
    "    top_p: Optional[float] = None                # 核采样参数\n",
    "    frequency_penalty: Optional[float] = None     # 频率惩罚\n",
    "    presence_penalty: Optional[float] = None      # 存在惩罚\n",
    "    stop: Optional[Union[str, List[str]]] = None  # 停止词\n",
    "    \n",
    "    # 工具和流式控制\n",
    "    tools: Optional[List[ChatCompletionTool]] = None  # 工具列表\n",
    "    stream: bool = False                          # 是否流式响应\n",
    "    \n",
    "    # 高级参数\n",
    "    logit_bias: Optional[Dict[str, int]] = None   # 词汇偏置\n",
    "    logprobs: Optional[bool] = None               # 返回概率\n",
    "    thinking: Optional[Thinking] = None           # 思考模式\n",
    "    metadata: Optional[Dict[str, Any]] = None     # 元数据\n",
    "    n: Optional[int] = Field(default=1, ge=1, le=5)  # 生成数量\n",
    "```\n",
    "\n",
    "**关键理解：**\n",
    "- `messages` 是核心，包含完整的对话历史\n",
    "- `model` 指定使用的 LLM 模型\n",
    "- 所有 ArkChatParameters 的字段都在这里\n",
    "- `stream` 控制响应模式（流式 vs 非流式）\n",
    "- `n` 参数影响成本，建议保持为1\n",
    "\n",
    "**数据流追踪：**\n",
    "```\n",
    "ArkChatParameters + Context.state → ArkChatRequest → LLM API → ArkChatResponse\n",
    "```\n",
    "\n",
    "**学习 ArkChatRequest 的原因：**\n",
    "- 理解参数如何传递给 LLM\n",
    "- 掌握请求的完整结构\n",
    "- 为调试和优化提供基础\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4225deef",
   "metadata": {
    "vscode": {
     "languageId": "raw"
    }
   },
   "source": [
    "### 📊 ArkChatResponse：响应和使用统计\n",
    "\n",
    "`ArkChatResponse` 类包含了 LLM 的响应结果和详细的使用统计信息。\n",
    "\n",
    "**核心字段解析：**\n",
    "\n",
    "```python\n",
    "class ArkChatResponse(Response):\n",
    "    id: str                                      # 响应唯一标识\n",
    "    choices: List[Choice]                        # 响应选择列表\n",
    "    created: int                                 # 创建时间戳\n",
    "    model: str                                   # 使用的模型\n",
    "    object: Literal[\"chat.completion\"]           # 对象类型\n",
    "    \n",
    "    # 使用统计\n",
    "    usage: Optional[CompletionUsage] = None      # 基本使用统计\n",
    "    bot_usage: Optional[BotUsage] = None         # 详细使用统计\n",
    "    metadata: Optional[Dict[str, Any]] = None    # 元数据\n",
    "```\n",
    "\n",
    "**CompletionUsage 详解：**\n",
    "```python\n",
    "class CompletionUsage(BaseModel):\n",
    "    prompt_tokens: int                           # 输入token数\n",
    "    completion_tokens: int                       # 输出token数\n",
    "    total_tokens: int                            # 总token数\n",
    "```\n",
    "\n",
    "**BotUsage 详解：**\n",
    "```python\n",
    "class BotUsage(BaseModel):\n",
    "    model_usage: Optional[List[CompletionUsage]] = None    # 模型使用统计\n",
    "    action_usage: Optional[List[ActionUsage]] = None       # 动作使用统计\n",
    "    action_details: Optional[List[ActionDetail]] = None    # 动作详细信息\n",
    "```\n",
    "\n",
    "**关键理解：**\n",
    "- `usage` 提供基本的token使用统计，直接关系到成本\n",
    "- `bot_usage` 提供更详细的使用信息，包括工具调用统计\n",
    "- `choices[0].message` 包含实际的响应内容\n",
    "- 响应中的统计信息对成本控制和性能优化至关重要\n",
    "\n",
    "**与其他概念的联系：**\n",
    "- ArkChatRequest 生成 ArkChatResponse\n",
    "- 使用统计帮助优化 ArkChatParameters\n",
    "- 工具调用会影响 action_usage 和 action_details\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4debd585",
   "metadata": {
    "vscode": {
     "languageId": "raw"
    }
   },
   "source": [
    "### 🎯 第 3 章总结\n",
    "\n",
    "通过本章的学习，您已经掌握了 Arkitect 框架的参数控制系统：\n",
    "\n",
    "#### 1. **ArkChatParameters（参数控制核心）**\n",
    "- ✅ 理解了所有核心参数的作用和取值范围\n",
    "- ✅ 掌握了针对不同任务的参数配置策略\n",
    "- ✅ 学会了通过参数优化 Agent 性能和成本\n",
    "\n",
    "#### 2. **核心参数深度理解**\n",
    "- ✅ **temperature**：控制创造性，工具调用用0.1-0.3，创意任务用0.7-1.2\n",
    "- ✅ **max_tokens**：控制输出长度和成本，根据任务需求设置\n",
    "- ✅ **frequency_penalty**：减少重复内容，适用于列表生成和创意写作\n",
    "- ✅ **stop**：精确控制输出结束点，提升用户体验\n",
    "\n",
    "#### 3. **ArkChatRequest（请求生命周期）**\n",
    "- ✅ 理解了参数如何从 ArkChatParameters 传递到 LLM\n",
    "- ✅ 掌握了请求的完整结构和数据流\n",
    "- ✅ 学会了调试和优化请求参数\n",
    "\n",
    "#### 4. **ArkChatResponse（响应和统计）**\n",
    "- ✅ 理解了响应结构和使用统计的重要性\n",
    "- ✅ 掌握了如何分析 token 使用量和成本\n",
    "- ✅ 学会了通过统计信息优化参数配置\n",
    "\n",
    "#### 🔑 **关键要点回顾**\n",
    "1. **参数配置 = 任务优化**：不同任务需要不同的参数组合\n",
    "2. **成本控制 = 智能设计**：通过参数控制实现成本效益最优化\n",
    "3. **统计分析 = 持续改进**：利用使用统计不断优化配置\n",
    "4. **数据流理解 = 调试能力**：掌握完整的请求-响应流程\n",
    "\n",
    "#### 🚀 **下一步**\n",
    "在下一章中，我们将学习如何使用 Hooks 系统深入观察和调试 Agent 的执行过程！\n",
    "\n",
    "---\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bdd283ce",
   "metadata": {},
   "source": [
    "## 第 4 章：高级调试 - 使用 Hooks 观察 Agent\n",
    "\n",
    "在前几章中，我们构建了 Agent 并控制了其行为。现在，让我们学习如何使用 Hooks 系统深入观察和调试 Agent 的执行过程。\n",
    "\n",
    "### 什么是 Hooks？\n",
    "Hooks 是 Arkitect 框架提供的“钩子”机制，允许您在 Agent 执行的关键点插入自定义逻辑。它们就像“监听器”，帮助您：\n",
    "- 🔍 **观察内部过程**：监控 LLM 调用、工具执行等\n",
    "- 📊 **性能分析**：记录时间、token 使用等指标\n",
    "- ⚠️ **错误处理**：捕获和干预异常\n",
    "- 🔧 **自定义行为**：在特定点修改数据流\n",
    "\n",
    "Hooks 是生产级应用的必备工具，尤其在调试复杂 Agent 时。\n",
    "\n",
    "### Hooks 的核心价值\n",
    "- 提供 Agent “黑箱”内部的可见性\n",
    "- 支持异步操作，不影响性能\n",
    "- 与 ContextInterruption 结合处理异常\n",
    "- 易于扩展，用于日志、监控等\n",
    "\n",
    "接下来，我们将学习四种主要 Hook 类型，并通过实际示例演示其用法。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "53778129",
   "metadata": {},
   "source": [
    "### 四种主要 Hook 类型\n",
    "\n",
    "Arkitect 提供了以下 Hook 类型，覆盖 Agent 执行的完整生命周期：\n",
    "\n",
    "1. **PreLLMCallHook**：在调用 LLM 前触发\n",
    "   - 用途：修改请求参数、记录输入\n",
    "   - 时机：Context 准备发送消息给 LLM 前\n",
    "\n",
    "2. **PostLLMCallHook**：LLM 调用后触发\n",
    "   - 用途：分析响应、处理工具调用决定\n",
    "   - 时机：收到 LLM 响应后\n",
    "\n",
    "3. **PreToolCallHook**：工具调用前触发\n",
    "   - 用途：验证工具参数、记录调用\n",
    "   - 时机：决定调用工具前\n",
    "\n",
    "4. **PostToolCallHook**：工具调用后触发\n",
    "   - 用途：处理工具结果、错误恢复\n",
    "   - 时机：工具执行完成后\n",
    "\n",
    "**关键理解**：\n",
    "- Hooks 通过 Context 的 `hooks` 参数注册\n",
    "- 可以同时注册多个 Hooks\n",
    "- Hooks 接收上下文参数，支持异步操作\n",
    "\n",
    "**与其他概念的联系**：\n",
    "- Hooks 可以访问 State 和 ArkMessage\n",
    "- 与 ContextInterruption 结合处理异常\n",
    "- 常用于分析 ArkChatRequest/Response"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "id": "1f873bdd",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "✅ 调试 Hooks 已创建！\n"
     ]
    }
   ],
   "source": [
    "# 示例：创建简单的打印 Hook\n",
    "\n",
    "\n",
    "# 示例：创建简单的打印 Hook\n",
    "# 注意：Hook只能返回State，不能返回其它类型\n",
    "# 注意：XXCallHook Class 下 xx_call 是空方法，需要重写\n",
    "\n",
    "\n",
    "class DebugPreLLMHook(PreLLMCallHook):\n",
    "    async def pre_llm_call(self, state: State) -> State:\n",
    "        print(\"🔍 Pre LLM Hook: 请求发送中...\")\n",
    "        print(f\"   消息数量: {len(state.messages)}\")\n",
    "        print(f\"   消息: {state.messages}\")\n",
    "        return state  # 返回 state，支持修改\n",
    "\n",
    "\n",
    "class DebugPostLLMHook(PostLLMCallHook):\n",
    "    async def post_llm_call(self, state: State) -> State:\n",
    "        print(\"📊 Post LLM Hook: 响应接收完成\")\n",
    "        if state.messages:\n",
    "            response_message = state.messages[-1]  # 最新响应\n",
    "            content = response_message.get(\"content\", \"\")\n",
    "            if not isinstance(content, str):\n",
    "                content = str(content)\n",
    "            print(f\"   响应: {content}...\")\n",
    "        else:\n",
    "            print(\"   无响应消息\")\n",
    "        return state\n",
    "\n",
    "\n",
    "class DebugPreToolHook(PreToolCallHook):\n",
    "    async def pre_tool_call(self, name: str, arguments: str, state: State) -> State:\n",
    "        print(f\"🛠️ Pre Tool Hook: 调用工具 {name}\")\n",
    "        print(f\"   参数: {arguments}\")\n",
    "        return state  # 返回参数，支持修改\n",
    "\n",
    "\n",
    "class DebugPostToolHook(PostToolCallHook):\n",
    "    async def post_tool_call(\n",
    "        self,\n",
    "        name: str,\n",
    "        arguments: str,\n",
    "        response: Any,\n",
    "        exception: Optional[Exception],\n",
    "        state: State,\n",
    "    ) -> State | Any:\n",
    "        print(f\"✅ Post Tool Hook: 工具 {name} 执行完成\")\n",
    "        if exception:\n",
    "            print(f\"❌ 异常: {str(exception)}\")  # 处理异常，如 tool_ability_tester\n",
    "            return \"错误：工具调用失败\"  # 自定义返回\n",
    "        print(f\"   结果预览: {str(response)[:50]}...\")\n",
    "        return state\n",
    "\n",
    "\n",
    "print(\"✅ 调试 Hooks 已创建！\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4eed9cd4",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "✅ 带 Hooks 的 Agent 已创建！\n"
     ]
    }
   ],
   "source": [
    "async def create_hooked_agent():\n",
    "    \"\"\"创建带 Hooks 的 Agent\"\"\"\n",
    "\n",
    "    # 创建 Context 并注册 Hooks\n",
    "    MODEL = \"doubao-seed-1-6-flash-250615\"\n",
    "    hooked_context = Context(\n",
    "        model=MODEL,\n",
    "        tools=[get_weather],\n",
    "    )\n",
    "    hooked_context.set_pre_llm_call_hook(DebugPreLLMHook())\n",
    "    hooked_context.set_post_llm_call_hook(DebugPostLLMHook())\n",
    "    hooked_context.set_pre_tool_call_hook(DebugPreToolHook())\n",
    "    hooked_context.set_post_tool_call_hook(DebugPostToolHook())\n",
    "    await hooked_context.init()\n",
    "\n",
    "    print(\"✅ 带 Hooks 的 Agent 已创建！\")\n",
    "    return hooked_context\n",
    "\n",
    "\n",
    "# 创建 Agent\n",
    "hooked_context = await create_hooked_agent()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "id": "1db6b251",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "🧪 测试带 Hooks 的 Agent\n",
      "========================================\n",
      "👤 用户: 北京的天气如何？\n",
      "🤖 Agent: \n",
      "🔍 Pre LLM Hook: 请求发送中...\n",
      "   消息数量: 1\n",
      "   消息: [{'role': 'user', 'content': '北京的天气如何？'}]\n",
      "📊 Post LLM Hook: 响应接收完成\n",
      "   响应: ...\n",
      "🛠️ Pre Tool Hook: 调用工具 get_weather\n",
      "   参数: {\"city\":\"北京\"}\n",
      "🛠️ Pre Tool Hook: 调用工具 get_weather\n",
      "   参数: {\"city\":\"北京\"}\n",
      "✅ Post Tool Hook: 工具 get_weather 执行完成\n",
      "   结果预览: meta=None content=[TextContent(type='text', text=\"...\n",
      "🔍 Pre LLM Hook: 请求发送中...\n",
      "   消息数量: 3\n",
      "   消息: [{'role': 'user', 'content': '北京的天气如何？'}, {'content': '', 'role': 'assistant', 'function_call': None, 'tool_calls': [{'index': 0, 'id': 'call_z6bxgzm84yb8lexg81uf0wyg', 'function': {'arguments': '{\"city\":\"北京\"}', 'name': 'get_weather'}, 'type': 'function'}], 'audio': None, 'reasoning_content': None}, {'role': 'tool', 'tool_call_id': 'call_z6bxgzm84yb8lexg81uf0wyg', 'content': \"([TextContent(type='text', text='北京今天的天气：晴天，气温 25°C，微风', annotations=None, meta=None)], {'result': '北京今天的天气：晴天，气温 25°C，微风'})\"}]\n",
      "北京今天的天气：晴天，气温 25°C，微风📊 Post LLM Hook: 响应接收完成\n",
      "   响应: 北京今天的天气：晴天，气温 25°C，微风...\n",
      "\n",
      "\n",
      "💡 注意观察控制台输出的 Hook 日志！\n"
     ]
    }
   ],
   "source": [
    "# 测试带 Hooks 的 Agent\n",
    "test_question = \"北京的天气如何？\"\n",
    "\n",
    "print(\"\\n🧪 测试带 Hooks 的 Agent\")\n",
    "print(\"=\" * 40)\n",
    "\n",
    "completion = await hooked_context.completions.create(\n",
    "    [{\"role\": \"user\", \"content\": test_question}], stream=True\n",
    ")\n",
    "\n",
    "print(f\"👤 用户: {test_question}\")\n",
    "print(\"🤖 Agent: \\n\", end=\"\")\n",
    "\n",
    "async for chunk in completion:\n",
    "    if hasattr(chunk, \"choices\") and chunk.choices:\n",
    "        delta = chunk.choices[0].delta\n",
    "        if hasattr(delta, \"content\") and delta.content:\n",
    "            print(delta.content, end=\"\")\n",
    "\n",
    "print(\"\\n\\n💡 注意观察控制台输出的 Hook 日志！\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a5975af5",
   "metadata": {},
   "source": [
    "#### 高级 Hook：性能监控与异常处理\n",
    "\n",
    "在实际工程中，Hook 常用于监控工具调用的性能、捕获异常并与 Agent 的中断机制结合。  \n",
    "这些能力本质上是对前面 Context、工具、异常机制的灵活组合。\n",
    "\n",
    "**常见场景：**\n",
    "- 统计每次工具调用的耗时\n",
    "- 捕获并记录工具调用异常\n",
    "- 遇到严重异常时优雅中断对话\n",
    "\n",
    "**实现思路：**\n",
    "- 在 `PreToolCallHook` 记录开始时间，在 `PostToolCallHook` 计算耗时并输出日志。\n",
    "- 在 `PostToolCallHook` 检查 `tool_call.exception`，如有异常则记录或抛出 `ContextInterruption`。\n",
    "- 这些 Hook 可与前面定义的 Agent/工具无缝结合，无需额外复杂代码。\n",
    "\n",
    "**示例代码片段：**\n",
    "```python\n",
    "class PerfAndErrorHook(PostToolCallHook):\n",
    "    async def __call__(self, context, tool_call, result):\n",
    "        if tool_call.exception:\n",
    "            print(f\"工具调用异常: {tool_call.exception}\")\n",
    "        else:\n",
    "            print(f\"工具 {tool_call.name} 正常完成\")\n",
    "```\n",
    "将此 Hook 加入 Context 后，所有工具调用都会被自动监控和异常捕获。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "edca368a",
   "metadata": {},
   "source": [
    "#### Hook 在自动化测试中的应用\n",
    "\n",
    "Hook 还可以用于自动化测试，记录每次对话的输入输出，或断言 Agent 行为是否符合预期。\n",
    "\n",
    "**常见场景：**\n",
    "- 自动记录对话内容，便于回归测试\n",
    "- 在测试用例中断言输出内容是否符合预期\n",
    "\n",
    "**推荐实现方式：**\n",
    "- 自定义一个 Hook，将输入输出保存到列表或日志\n",
    "- 在测试用例中直接断言这些记录"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "id": "ea35e1c6",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "🧪 自动化测试 Hook 演示\n",
      "==================================================\n",
      "【测试记录】输入: [{'role': 'user', 'content': '你好，今天的天气如何'}, {'content': '要查询今天的天气，你可以通过以下几种方式：\\n### 1. 手机天气应用\\n打开手机自带的天气应用程序（如苹果的“天气”、安卓的天气相关应用），一般会自动定位并显示当地实时天气。\\n\\n### 2. 搜索引擎查询\\n打开浏览器，在搜索框中输入“[你所在城市]今天天气”，比如“北京今天天气”，点击搜索结果就能看到详细的天气信息（包括温度、阴晴状况、风力等）。\\n\\n### 3. 天气网站\\n访问专业的天气网站，如中国天气网（https://www.weather.com.cn/），在网站上输入所在城市名称，即可获取今天的天气详情。', 'role': 'assistant', 'function_call': None, 'tool_calls': None, 'audio': None, 'reasoning_content': '用户现在想知道今天的天气，但我没办法直接获取实时天气呢。用户可以通过打开手机的天气应用，或者在浏览器里搜索当地的天气查询网站，这样就能查到准确的今天当地天气情况啦。'}]，输出: {'content': '要查询今天的天气，你可以通过以下几种方式：\\n### 1. 手机天气应用\\n打开手机自带的天气应用程序（如苹果的“天气”、安卓的天气相关应用），一般会自动定位并显示当地实时天气。\\n\\n### 2. 搜索引擎查询\\n打开浏览器，在搜索框中输入“[你所在城市]今天天气”，比如“北京今天天气”，点击搜索结果就能看到详细的天气信息（包括温度、阴晴状况、风力等）。\\n\\n### 3. 天气网站\\n访问专业的天气网站，如中国天气网（https://www.weather.com.cn/），在网站上输入所在城市名称，即可获取今天的天气详情。', 'role': 'assistant', 'function_call': None, 'tool_calls': None, 'audio': None, 'reasoning_content': '用户现在想知道今天的天气，但我没办法直接获取实时天气呢。用户可以通过打开手机的天气应用，或者在浏览器里搜索当地的天气查询网站，这样就能查到准确的今天当地天气情况啦。'}\n",
      "AI 回复: 要查询今天的天气，你可以通过以下几种方式：\n",
      "### 1. 手机天气应用\n",
      "打开手机自带的天气应用程序（如苹果的“天气”、安卓的天气相关应用），一般会自动定位并显示当地实时天气。\n",
      "\n",
      "### 2. 搜索引擎查询\n",
      "打开浏览器，在搜索框中输入“[你所在城市]今天天气”，比如“北京今天天气”，点击搜索结果就能看到详细的天气信息（包括温度、阴晴状况、风力等）。\n",
      "\n",
      "### 3. 天气网站\n",
      "访问专业的天气网站，如中国天气网（https://www.weather.com.cn/），在网站上输入所在城市名称，即可获取今天的天气详情。\n"
     ]
    },
    {
     "ename": "AssertionError",
     "evalue": "回复内容不符合预期",
     "output_type": "error",
     "traceback": [
      "\u001b[31m---------------------------------------------------------------------------\u001b[39m",
      "\u001b[31mAssertionError\u001b[39m                            Traceback (most recent call last)",
      "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[30]\u001b[39m\u001b[32m, line 28\u001b[39m\n\u001b[32m     25\u001b[39m     \u001b[38;5;28;01massert\u001b[39;00m \u001b[33m\"\u001b[39m\u001b[33m人工智能\u001b[39m\u001b[33m\"\u001b[39m \u001b[38;5;129;01min\u001b[39;00m reply \u001b[38;5;129;01mand\u001b[39;00m \u001b[33m\"\u001b[39m\u001b[33mAI\u001b[39m\u001b[33m\"\u001b[39m \u001b[38;5;129;01min\u001b[39;00m reply, \u001b[33m\"\u001b[39m\u001b[33m回复内容不符合预期\u001b[39m\u001b[33m\"\u001b[39m\n\u001b[32m     27\u001b[39m \u001b[38;5;66;03m# 运行测试\u001b[39;00m\n\u001b[32m---> \u001b[39m\u001b[32m28\u001b[39m \u001b[38;5;28;01mawait\u001b[39;00m test_agent_reply()\n",
      "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[30]\u001b[39m\u001b[32m, line 25\u001b[39m, in \u001b[36mtest_agent_reply\u001b[39m\u001b[34m()\u001b[39m\n\u001b[32m     23\u001b[39m reply = completion.choices[\u001b[32m0\u001b[39m].message.content\n\u001b[32m     24\u001b[39m \u001b[38;5;28mprint\u001b[39m(\u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[33mAI 回复: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mreply\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m\"\u001b[39m)\n\u001b[32m---> \u001b[39m\u001b[32m25\u001b[39m \u001b[38;5;28;01massert\u001b[39;00m \u001b[33m\"\u001b[39m\u001b[33m人工智能\u001b[39m\u001b[33m\"\u001b[39m \u001b[38;5;129;01min\u001b[39;00m reply \u001b[38;5;129;01mand\u001b[39;00m \u001b[33m\"\u001b[39m\u001b[33mAI\u001b[39m\u001b[33m\"\u001b[39m \u001b[38;5;129;01min\u001b[39;00m reply, \u001b[33m\"\u001b[39m\u001b[33m回复内容不符合预期\u001b[39m\u001b[33m\"\u001b[39m\n",
      "\u001b[31mAssertionError\u001b[39m: 回复内容不符合预期"
     ]
    }
   ],
   "source": [
    "class TestRecorderHook(PostLLMCallHook):\n",
    "    async def post_llm_call(self, state: State) -> State:\n",
    "        print(f\"【测试记录】输入: {state.messages}，输出: {state.messages[-1]}\")\n",
    "        return state\n",
    "\n",
    "\n",
    "# 在测试用例中断言输出\n",
    "async def test_agent_reply():\n",
    "    print(\"🧪 自动化测试 Hook 演示\")\n",
    "    print(\"=\" * 50)\n",
    "\n",
    "    from arkitect.core.component.context.context import Context\n",
    "\n",
    "    context = Context(model=\"doubao-seed-1-6-flash-250615\")\n",
    "    await context.init()\n",
    "    context.set_post_llm_call_hook(TestRecorderHook())\n",
    "\n",
    "    # 发送消息并断言输出内容\n",
    "    completion = await context.completions.create(\n",
    "        [{\"role\": \"user\", \"content\": \"你好，今天的天气如何\"}], stream=False\n",
    "    )\n",
    "    reply = completion.choices[0].message.content\n",
    "    print(f\"AI 回复: {reply}\")\n",
    "    assert \"人工智能\" in reply and \"AI\" in reply, \"回复内容不符合预期\"\n",
    "\n",
    "\n",
    "# 运行测试\n",
    "await test_agent_reply()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8af81ce0",
   "metadata": {},
   "source": [
    "### Hook 最佳实践\n",
    "- **日志记录**：使用 Hooks 记录所有关键事件\n",
    "- **性能优化**：监控耗时长的操作\n",
    "- **错误恢复**：实现自动重试机制\n",
    "- **不要阻塞**：保持 Hooks 异步且高效"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "07be7ddb",
   "metadata": {},
   "source": [
    "### 🎯 第 4 章总结\n",
    "\n",
    "通过本章，您掌握了 Hooks 系统：\n",
    "- 理解了四种 Hook 类型及其生命周期\n",
    "- 学会了创建和应用自定义 Hooks\n",
    "- 掌握了性能监控和错误处理技巧\n",
    "- 了解了 Hooks 与 ContextInterruption 的结合\n",
    "\n",
    "**下一步**： Cookbook 即将结束，请查看 Q&A 和结语！"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e22e41c3",
   "metadata": {},
   "source": [
    "## Q&A：常见问题和解决方案\n",
    "\n",
    "这里汇总了使用 Arkitect 框架的常见问题及解决方案。\n",
    "\n",
    "1. **Q: 如何处理工具调用异常？**  \n",
    "   A: 使用 PostToolCallHook 捕获异常，或在工具函数中返回错误字符串。参考第4章错误处理示例。\n",
    "\n",
    "2. **Q: temperature 参数如何选择？**  \n",
    "   A: 工具任务用 0.1-0.3，创意任务用 0.7-1.0。参考第3章实验。\n",
    "\n",
    "3. **Q: 为什么 stream=True 时无 usage？**  \n",
    "   A: 流式模式不返回 usage，使用 stream=False 获取。参考第3章说明。\n",
    "\n",
    "4. **Q: 如何自定义 System Prompt？**  \n",
    "   A: 在 messages 列表中添加 role=\"system\" 消息。参考第1章示例。\n",
    "\n",
    "5. **Q: Agent 内存占用高如何优化？**  \n",
    "   A: 定期清除 context.state.messages，或使用 max_tokens 限制输出。\n",
    "\n",
    "6. **Q: Hooks 影响性能吗？**  \n",
    "   A: 轻量 Hooks 无影响，避免在 Hooks 中添加重计算。\n",
    "\n",
    "7. **Q: 如何测试多模型兼容？**  \n",
    "   A: 在 Context 初始化时切换 model 参数，并运行相同测试。\n",
    "\n",
    "更多问题？欢迎在 GitHub Issues 反馈！\n",
    "\n",
    "---\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d2b1c3b1",
   "metadata": {},
   "source": [
    "## 结语：您的 Arkitect 之旅\n",
    "\n",
    "恭喜！您已完成 Arkitect Cookbook 的核心内容。从基础 Context 到高级 Hooks，您现在具备构建强大 AI Agent 的能力。\n",
    "\n",
    "### 关键收获\n",
    "- **基础**：掌握 Context、State 和消息管理\n",
    "- **构建**：创建工具驱动的 Agent\n",
    "- **控制**：通过参数优化行为\n",
    "- **调试**：使用 Hooks 监控一切\n",
    "\n",
    "### 行动号召\n",
    "- 实践：基于本教程构建您的第一个生产 Agent\n",
    "- 扩展：探索更多模型和工具集成\n",
    "- 贡献：分享您的经验或改进建议\n",
    "\n",
    "感谢您的学习！如果有疑问，随时重温 Q&A 或联系我们。开始构建属于您自己的Agent吧！🚀"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python (volcano)",
   "language": "python",
   "name": "volcano"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.13"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
